diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/dom | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/dom')
489 files changed, 27278 insertions, 22012 deletions
diff --git a/Source/WebCore/dom/MessagePortChannel.cpp b/Source/WebCore/dom/ActiveDOMCallback.cpp index c4da6280f..5b1741e13 100644 --- a/Source/WebCore/dom/MessagePortChannel.cpp +++ b/Source/WebCore/dom/ActiveDOMCallback.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 2010. 2012 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -27,9 +27,27 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + #include "config.h" -#include "MessagePortChannel.h" +#include "ActiveDOMCallback.h" + +#include "ScriptExecutionContext.h" namespace WebCore { +ActiveDOMCallback::ActiveDOMCallback(ScriptExecutionContext* context) + : ContextDestructionObserver(context) +{ +} + +ActiveDOMCallback::~ActiveDOMCallback() +{ +} + +bool ActiveDOMCallback::canInvokeCallback() const +{ + ScriptExecutionContext* context = scriptExecutionContext(); + return context && !context->activeDOMObjectsAreSuspended() && !context->activeDOMObjectsAreStopped(); +} + } // namespace WebCore diff --git a/Source/WebCore/dom/ExceptionCodePlaceholder.cpp b/Source/WebCore/dom/ActiveDOMCallback.h index 71750254d..566b40a1f 100644 --- a/Source/WebCore/dom/ExceptionCodePlaceholder.cpp +++ b/Source/WebCore/dom/ActiveDOMCallback.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2010, 2012 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -28,25 +28,25 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "config.h" -#include "ExceptionCodePlaceholder.h" +#pragma once -namespace WebCore { +#include "ContextDestructionObserver.h" -#if !ASSERT_DISABLED +namespace WebCore { -NoExceptionAssertionChecker::NoExceptionAssertionChecker(const char* file, int line) - : ExceptionCodePlaceholder(defaultExceptionCode) - , m_file(file) - , m_line(line) -{ -} +class ScriptExecutionContext; -NoExceptionAssertionChecker::~NoExceptionAssertionChecker() -{ - ASSERT_AT(!m_code || m_code == defaultExceptionCode, m_file, m_line, ""); -} +// A base class that prevents binding callbacks from executing when +// active dom objects are stopped or suspended. +// +// Should only be created, used, and destroyed on the script execution +// context thread. +class ActiveDOMCallback : public ContextDestructionObserver { +public: + ActiveDOMCallback(ScriptExecutionContext* context); + virtual ~ActiveDOMCallback(); -#endif + bool canInvokeCallback() const; +}; -} +} // namespace WebCore diff --git a/Source/WebCore/dom/ActiveDOMCallbackMicrotask.cpp b/Source/WebCore/dom/ActiveDOMCallbackMicrotask.cpp new file mode 100644 index 000000000..66f590a49 --- /dev/null +++ b/Source/WebCore/dom/ActiveDOMCallbackMicrotask.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ActiveDOMCallbackMicrotask.h" + +namespace WebCore { + +ActiveDOMCallbackMicrotask::ActiveDOMCallbackMicrotask(MicrotaskQueue& queue, ScriptExecutionContext& scriptExecutionContext, Function<void()>&& task) + : ActiveDOMCallback(&scriptExecutionContext) + , m_queue(queue) + , m_task(WTFMove(task)) +{ +} + +ActiveDOMCallbackMicrotask::~ActiveDOMCallbackMicrotask() +{ +} + +Microtask::Result ActiveDOMCallbackMicrotask::run() +{ + if (!canInvokeCallback()) + return Result::KeepInQueue; + + m_task(); + return Result::Done; +} + +void ActiveDOMCallbackMicrotask::contextDestroyed() +{ + // NOTE: Calling the function below will cause this to be destroyed. + removeSelfFromQueue(m_queue); +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/ActiveDOMCallbackMicrotask.h b/Source/WebCore/dom/ActiveDOMCallbackMicrotask.h new file mode 100644 index 000000000..5f4688969 --- /dev/null +++ b/Source/WebCore/dom/ActiveDOMCallbackMicrotask.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "ActiveDOMCallback.h" +#include "Microtasks.h" +#include <wtf/Function.h> + +namespace WebCore { + +class ActiveDOMCallbackMicrotask final : public Microtask, public ActiveDOMCallback { + WTF_MAKE_FAST_ALLOCATED; +public: + WEBCORE_EXPORT ActiveDOMCallbackMicrotask(MicrotaskQueue&, ScriptExecutionContext&, WTF::Function<void ()>&&); + WEBCORE_EXPORT virtual ~ActiveDOMCallbackMicrotask(); + + Result run() override; + +private: + void contextDestroyed() override; + + // FIXME: It should not be necessary to have the queue as a member. Instead, it should + // be accessed via the ScriptExecutionContext, which should hold a reference to the relevent + // queue. + MicrotaskQueue& m_queue; + WTF::Function<void ()> m_task; +}; + +} // namespace WebCore diff --git a/Source/WebCore/dom/ActiveDOMObject.cpp b/Source/WebCore/dom/ActiveDOMObject.cpp index 0c6e8ed02..d6cfe2971 100644 --- a/Source/WebCore/dom/ActiveDOMObject.cpp +++ b/Source/WebCore/dom/ActiveDOMObject.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -28,8 +28,6 @@ #include "ActiveDOMObject.h" #include "ScriptExecutionContext.h" -#include "WorkerGlobalScope.h" -#include "WorkerThread.h" namespace WebCore { @@ -37,14 +35,14 @@ ActiveDOMObject::ActiveDOMObject(ScriptExecutionContext* scriptExecutionContext) : ContextDestructionObserver(scriptExecutionContext) , m_pendingActivityCount(0) #if !ASSERT_DISABLED - , m_suspendIfNeededCalled(false) + , m_suspendIfNeededWasCalled(false) #endif { if (!m_scriptExecutionContext) return; ASSERT(m_scriptExecutionContext->isContextThread()); - m_scriptExecutionContext->didCreateActiveDOMObject(this); + m_scriptExecutionContext->didCreateActiveDOMObject(*this); } ActiveDOMObject::~ActiveDOMObject() @@ -52,7 +50,7 @@ ActiveDOMObject::~ActiveDOMObject() if (!m_scriptExecutionContext) return; - ASSERT(m_suspendIfNeededCalled); + ASSERT(m_suspendIfNeededWasCalled); // ActiveDOMObject may be inherited by a sub-class whose life-cycle // exceeds that of the associated ScriptExecutionContext. In those cases, @@ -62,28 +60,37 @@ ActiveDOMObject::~ActiveDOMObject() // here. if (m_scriptExecutionContext) { ASSERT(m_scriptExecutionContext->isContextThread()); - m_scriptExecutionContext->willDestroyActiveDOMObject(this); + m_scriptExecutionContext->willDestroyActiveDOMObject(*this); } } void ActiveDOMObject::suspendIfNeeded() { #if !ASSERT_DISABLED - ASSERT(!m_suspendIfNeededCalled); - m_suspendIfNeededCalled = true; + ASSERT(!m_suspendIfNeededWasCalled); + m_suspendIfNeededWasCalled = true; #endif if (!m_scriptExecutionContext) return; - m_scriptExecutionContext->suspendActiveDOMObjectIfNeeded(this); + m_scriptExecutionContext->suspendActiveDOMObjectIfNeeded(*this); } +#if !ASSERT_DISABLED + +void ActiveDOMObject::assertSuspendIfNeededWasCalled() const +{ + ASSERT(m_suspendIfNeededWasCalled); +} + +#endif + bool ActiveDOMObject::hasPendingActivity() const { return m_pendingActivityCount; } -bool ActiveDOMObject::canSuspend() const +bool ActiveDOMObject::canSuspendForDocumentSuspension() const { return false; } diff --git a/Source/WebCore/dom/ActiveDOMObject.h b/Source/WebCore/dom/ActiveDOMObject.h index 5a1f3d121..c47b1f45a 100644 --- a/Source/WebCore/dom/ActiveDOMObject.h +++ b/Source/WebCore/dom/ActiveDOMObject.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,8 +24,7 @@ * */ -#ifndef ActiveDOMObject_h -#define ActiveDOMObject_h +#pragma once #include "ContextDestructionObserver.h" #include <wtf/Assertions.h> @@ -35,40 +34,46 @@ namespace WebCore { class ActiveDOMObject : public ContextDestructionObserver { public: - explicit ActiveDOMObject(ScriptExecutionContext*); - - // suspendIfNeeded() should be called exactly once after object construction to synchronize - // the suspend state with that in ScriptExecutionContext. + // The suspendIfNeeded must be called exactly once after object construction to update + // the suspended state to match that of the ScriptExecutionContext. void suspendIfNeeded(); -#if !ASSERT_DISABLED - bool suspendIfNeededCalled() const { return m_suspendIfNeededCalled; } -#endif + void assertSuspendIfNeededWasCalled() const; virtual bool hasPendingActivity() const; - // canSuspend() is used by the caller if there is a choice between suspending and stopping. - // For example, a page won't be suspended and placed in the back/forward cache if it has - // the objects that can not be suspended. - // However, 'suspend' can be called even if canSuspend() would return 'false'. That - // happens in step-by-step JS debugging for example - in this case it would be incorrect - // to stop the object. Exact semantics of suspend is up to the object then. + // The canSuspendForDocumentSuspension() function is used by the caller if there is a choice between suspending + // and stopping. For example, a page won't be suspended and placed in the back/forward + // cache if it contains any objects that cannot be suspended. + + // However, the suspend function will sometimes be called even if canSuspendForDocumentSuspension() returns false. + // That happens in step-by-step JS debugging for example - in this case it would be incorrect + // to stop the object. Exact semantics of suspend is up to the object in cases like that. + enum ReasonForSuspension { JavaScriptDebuggerPaused, WillDeferLoading, - DocumentWillBecomeInactive, + PageCache, PageWillBeSuspended, - DocumentWillBePaused }; - virtual bool canSuspend() const; + + virtual const char* activeDOMObjectName() const = 0; + + // These three functions must not have a side effect of creating or destroying + // any ActiveDOMObject. That means they must not result in calls to arbitrary JavaScript. + virtual bool canSuspendForDocumentSuspension() const = 0; // Returning false in canSuspendForDocumentSuspension() will prevent the page from entering the PageCache. virtual void suspend(ReasonForSuspension); virtual void resume(); + + // This function must not have a side effect of creating an ActiveDOMObject. + // That means it must not result in calls to arbitrary JavaScript. + // It can, however, have a side effect of deleting an ActiveDOMObject. virtual void stop(); template<class T> void setPendingActivity(T* thisObject) { ASSERT(thisObject == this); thisObject->ref(); - m_pendingActivityCount++; + ++m_pendingActivityCount; } template<class T> void unsetPendingActivity(T* thisObject) @@ -79,15 +84,22 @@ public: } protected: + explicit ActiveDOMObject(ScriptExecutionContext*); virtual ~ActiveDOMObject(); private: unsigned m_pendingActivityCount; #if !ASSERT_DISABLED - bool m_suspendIfNeededCalled; + bool m_suspendIfNeededWasCalled; #endif }; -} // namespace WebCore +#if ASSERT_DISABLED + +inline void ActiveDOMObject::assertSuspendIfNeededWasCalled() const +{ +} -#endif // ActiveDOMObject_h +#endif + +} // namespace WebCore diff --git a/Source/WebCore/dom/AllDescendantsCollection.h b/Source/WebCore/dom/AllDescendantsCollection.h new file mode 100644 index 000000000..22ed9d89b --- /dev/null +++ b/Source/WebCore/dom/AllDescendantsCollection.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "CachedHTMLCollection.h" + +namespace WebCore { + +class AllDescendantsCollection : public CachedHTMLCollection<AllDescendantsCollection, CollectionTypeTraits<AllDescendants>::traversalType> { +public: + static Ref<AllDescendantsCollection> create(ContainerNode& rootNode, CollectionType type) + { + ASSERT_UNUSED(type, type == AllDescendants); + return adoptRef(*new AllDescendantsCollection(rootNode, type)); + } + + bool elementMatches(Element&) const { return true; } + +protected: + AllDescendantsCollection(ContainerNode& rootNode, CollectionType type) + : CachedHTMLCollection<AllDescendantsCollection, CollectionTypeTraits<AllDescendants>::traversalType>(rootNode, type) + { } +}; + +} diff --git a/Source/WebCore/dom/AnimationEvent.cpp b/Source/WebCore/dom/AnimationEvent.cpp new file mode 100644 index 000000000..67d2006f9 --- /dev/null +++ b/Source/WebCore/dom/AnimationEvent.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "AnimationEvent.h" + +namespace WebCore { + +AnimationEvent::AnimationEvent(const AtomicString& type, const Init& initializer, IsTrusted isTrusted) + : Event(type, initializer, isTrusted) + , m_animationName(initializer.animationName) + , m_elapsedTime(initializer.elapsedTime) +{ +} + +AnimationEvent::AnimationEvent(const AtomicString& type, const String& animationName, double elapsedTime) + : Event(type, true, false) + , m_animationName(animationName) + , m_elapsedTime(elapsedTime) +{ +} + +AnimationEvent::~AnimationEvent() +{ +} + +const String& AnimationEvent::animationName() const +{ + return m_animationName; +} + +double AnimationEvent::elapsedTime() const +{ + return m_elapsedTime; +} + +EventInterface AnimationEvent::eventInterface() const +{ + return AnimationEventInterfaceType; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/AnimationEvent.h b/Source/WebCore/dom/AnimationEvent.h new file mode 100644 index 000000000..52bebf428 --- /dev/null +++ b/Source/WebCore/dom/AnimationEvent.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "Event.h" + +namespace WebCore { + +class AnimationEvent final : public Event { +public: + static Ref<AnimationEvent> create(const AtomicString& type, const String& animationName, double elapsedTime) + { + return adoptRef(*new AnimationEvent(type, animationName, elapsedTime)); + } + + struct Init : EventInit { + String animationName; + double elapsedTime { 0 }; + }; + + static Ref<AnimationEvent> create(const AtomicString& type, const Init& initializer, IsTrusted isTrusted = IsTrusted::No) + { + return adoptRef(*new AnimationEvent(type, initializer, isTrusted)); + } + + virtual ~AnimationEvent(); + + const String& animationName() const; + double elapsedTime() const; + + EventInterface eventInterface() const override; + +private: + AnimationEvent(const AtomicString& type, const String& animationName, double elapsedTime); + AnimationEvent(const AtomicString&, const Init&, IsTrusted); + + String m_animationName; + double m_elapsedTime; +}; + +} // namespace WebCore diff --git a/Source/WebCore/dom/AnimationEvent.idl b/Source/WebCore/dom/AnimationEvent.idl new file mode 100644 index 000000000..a50c4cece --- /dev/null +++ b/Source/WebCore/dom/AnimationEvent.idl @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Constructor(DOMString type, optional AnimationEventInit animationEventInitDict), +] interface AnimationEvent : Event { + readonly attribute DOMString animationName; + readonly attribute double elapsedTime; + + // FIXME: This is not supported yet. + // readonly attribute DOMString pseudoElement; +}; + +dictionary AnimationEventInit : EventInit { + DOMString animationName = ""; + double elapsedTime = 0.0; + + // FIXME: This is not supported yet. + // DOMString pseudoElement = ""; +}; + diff --git a/Source/WebCore/dom/Attr.cpp b/Source/WebCore/dom/Attr.cpp index ea9f35716..392f34220 100644 --- a/Source/WebCore/dom/Attr.cpp +++ b/Source/WebCore/dom/Attr.cpp @@ -23,7 +23,10 @@ #include "config.h" #include "Attr.h" +#include "AttributeChangeInvalidation.h" +#include "Event.h" #include "ExceptionCode.h" +#include "NoEventDispatchAssertion.h" #include "ScopedEventQueue.h" #include "StyleProperties.h" #include "StyledElement.h" @@ -36,35 +39,32 @@ namespace WebCore { using namespace HTMLNames; -Attr::Attr(Element* element, const QualifiedName& name) - : ContainerNode(&element->document()) - , m_element(element) +Attr::Attr(Element& element, const QualifiedName& name) + : ContainerNode(element.document()) + , m_element(&element) , m_name(name) - , m_ignoreChildrenChanged(0) { } Attr::Attr(Document& document, const QualifiedName& name, const AtomicString& standaloneValue) - : ContainerNode(&document) - , m_element(0) + : ContainerNode(document) , m_name(name) , m_standaloneValue(standaloneValue) - , m_ignoreChildrenChanged(0) { } -PassRefPtr<Attr> Attr::create(Element* element, const QualifiedName& name) +Ref<Attr> Attr::create(Element& element, const QualifiedName& name) { - RefPtr<Attr> attr = adoptRef(new Attr(element, name)); + Ref<Attr> attr = adoptRef(*new Attr(element, name)); attr->createTextChild(); - return attr.release(); + return attr; } -PassRefPtr<Attr> Attr::create(Document& document, const QualifiedName& name, const AtomicString& value) +Ref<Attr> Attr::create(Document& document, const QualifiedName& name, const AtomicString& value) { - RefPtr<Attr> attr = adoptRef(new Attr(document, name, value)); + Ref<Attr> attr = adoptRef(*new Attr(document, name, value)); attr->createTextChild(); - return attr.release(); + return attr; } Attr::~Attr() @@ -75,34 +75,31 @@ void Attr::createTextChild() { ASSERT(refCount()); if (!value().isEmpty()) { - RefPtr<Text> textNode = document().createTextNode(value().string()); + auto textNode = document().createTextNode(value().string()); // This does everything appendChild() would do in this situation (assuming m_ignoreChildrenChanged was set), // but much more efficiently. textNode->setParentNode(this); - setFirstChild(textNode.get()); - setLastChild(textNode.get()); + setFirstChild(textNode.ptr()); + setLastChild(textNode.ptr()); } } -void Attr::setPrefix(const AtomicString& prefix, ExceptionCode& ec) +ExceptionOr<void> Attr::setPrefix(const AtomicString& prefix) { - ec = 0; - checkSetPrefix(prefix, ec); - if (ec) - return; + auto result = checkSetPrefix(prefix); + if (result.hasException()) + return result.releaseException(); - if ((prefix == xmlnsAtom && namespaceURI() != XMLNSNames::xmlnsNamespaceURI) - || static_cast<Attr*>(this)->qualifiedName() == xmlnsAtom) { - ec = NAMESPACE_ERR; - return; - } + if ((prefix == xmlnsAtom && namespaceURI() != XMLNSNames::xmlnsNamespaceURI) || qualifiedName() == xmlnsAtom) + return Exception { NAMESPACE_ERR }; const AtomicString& newPrefix = prefix.isEmpty() ? nullAtom : prefix; - if (m_element) elementAttribute().setPrefix(newPrefix); m_name.setPrefix(newPrefix); + + return { }; } void Attr::setValue(const AtomicString& value) @@ -110,9 +107,10 @@ void Attr::setValue(const AtomicString& value) EventQueueScope scope; m_ignoreChildrenChanged++; removeChildren(); - if (m_element) + if (m_element) { + Style::AttributeChangeInvalidation styleInvalidation(*m_element, qualifiedName(), elementAttribute().value(), value); elementAttribute().setValue(value); - else + } else m_standaloneValue = value; createTextChild(); m_ignoreChildrenChanged--; @@ -120,40 +118,33 @@ void Attr::setValue(const AtomicString& value) invalidateNodeListAndCollectionCachesInAncestors(&m_name, m_element); } -void Attr::setValue(const AtomicString& value, ExceptionCode&) +void Attr::setValueForBindings(const AtomicString& value) { AtomicString oldValue = this->value(); if (m_element) m_element->willModifyAttribute(qualifiedName(), oldValue, value); - setValue(value); - if (m_element) m_element->didModifyAttribute(qualifiedName(), oldValue, value); } -void Attr::setNodeValue(const String& v, ExceptionCode& ec) +ExceptionOr<void> Attr::setNodeValue(const String& value) { - setValue(v, ec); + setValueForBindings(value); + return { }; } -PassRefPtr<Node> Attr::cloneNode(bool /*deep*/) +Ref<Node> Attr::cloneNodeInternal(Document& targetDocument, CloningOperation) { - RefPtr<Attr> clone = adoptRef(new Attr(document(), qualifiedName(), value())); - cloneChildNodes(clone.get()); - return clone.release(); + Ref<Attr> clone = adoptRef(*new Attr(targetDocument, qualifiedName(), value())); + cloneChildNodes(clone); + return WTFMove(clone); } // DOM Section 1.1.1 bool Attr::childTypeAllowed(NodeType type) const { - switch (type) { - case TEXT_NODE: - case ENTITY_REFERENCE_NODE: - return true; - default: - return false; - } + return type == TEXT_NODE; } void Attr::childrenChanged(const ChildChange&) @@ -164,34 +155,32 @@ void Attr::childrenChanged(const ChildChange&) invalidateNodeListAndCollectionCachesInAncestors(&qualifiedName(), m_element); StringBuilder valueBuilder; - TextNodeTraversal::appendContents(this, valueBuilder); + TextNodeTraversal::appendContents(*this, valueBuilder); AtomicString oldValue = value(); AtomicString newValue = valueBuilder.toAtomicString(); if (m_element) m_element->willModifyAttribute(qualifiedName(), oldValue, newValue); - if (m_element) + if (m_element) { + Style::AttributeChangeInvalidation styleInvalidation(*m_element, qualifiedName(), oldValue, newValue); elementAttribute().setValue(newValue); - else + } else m_standaloneValue = newValue; - if (m_element) + if (m_element) { + NoEventDispatchAssertion::DisableAssertionsInScope allowedScope; m_element->attributeChanged(qualifiedName(), oldValue, newValue); -} - -bool Attr::isId() const -{ - return qualifiedName().matches(document().idAttributeName()); + } } CSSStyleDeclaration* Attr::style() { // This function only exists to support the Obj-C bindings. - if (!m_element || !m_element->isStyledElement()) + if (!is<StyledElement>(m_element)) return nullptr; m_style = MutableStyleProperties::create(); - toStyledElement(m_element)->collectStyleForPresentationAttribute(qualifiedName(), value(), *m_style); + downcast<StyledElement>(*m_element).collectStyleForPresentationAttribute(qualifiedName(), value(), *m_style); return m_style->ensureCSSStyleDeclaration(); } @@ -214,13 +203,13 @@ void Attr::detachFromElementWithValue(const AtomicString& value) ASSERT(m_element); ASSERT(m_standaloneValue.isNull()); m_standaloneValue = value; - m_element = 0; + m_element = nullptr; } -void Attr::attachToElement(Element* element) +void Attr::attachToElement(Element& element) { ASSERT(!m_element); - m_element = element; + m_element = &element; m_standaloneValue = nullAtom; } diff --git a/Source/WebCore/dom/Attr.h b/Source/WebCore/dom/Attr.h index 3e34cca66..f2715995e 100644 --- a/Source/WebCore/dom/Attr.h +++ b/Source/WebCore/dom/Attr.h @@ -22,18 +22,18 @@ * */ -#ifndef Attr_h -#define Attr_h +#pragma once #include "ContainerNode.h" #include "QualifiedName.h" namespace WebCore { +class Attribute; class CSSStyleDeclaration; class MutableStyleProperties; -// Attr can have Text and EntityReference children +// Attr can have Text children // therefore it has to be a fullblown Node. The plan // is to dynamically allocate a textchild and store the // resulting nodevalue in the attribute upon @@ -41,69 +41,65 @@ class MutableStyleProperties; class Attr final : public ContainerNode { public: - static PassRefPtr<Attr> create(Element*, const QualifiedName&); - static PassRefPtr<Attr> create(Document&, const QualifiedName&, const AtomicString& value); + static Ref<Attr> create(Element&, const QualifiedName&); + static Ref<Attr> create(Document&, const QualifiedName&, const AtomicString& value); virtual ~Attr(); String name() const { return qualifiedName().toString(); } bool specified() const { return true; } Element* ownerElement() const { return m_element; } - const AtomicString& value() const; - void setValue(const AtomicString&, ExceptionCode&); + WEBCORE_EXPORT const AtomicString& value() const; void setValue(const AtomicString&); + const AtomicString& valueForBindings() const { return value(); } + WEBCORE_EXPORT void setValueForBindings(const AtomicString&); const QualifiedName& qualifiedName() const { return m_name; } - bool isId() const; + WEBCORE_EXPORT CSSStyleDeclaration* style(); - CSSStyleDeclaration* style(); - - void attachToElement(Element*); + void attachToElement(Element&); void detachFromElementWithValue(const AtomicString&); - virtual const AtomicString& namespaceURI() const override { return m_name.namespaceURI(); } + const AtomicString& namespaceURI() const final { return m_name.namespaceURI(); } + const AtomicString& localName() const final { return m_name.localName(); } + const AtomicString& prefix() const final { return m_name.prefix(); } private: - Attr(Element*, const QualifiedName&); + Attr(Element&, const QualifiedName&); Attr(Document&, const QualifiedName&, const AtomicString& value); void createTextChild(); - virtual String nodeName() const override { return name(); } - virtual NodeType nodeType() const override { return ATTRIBUTE_NODE; } + String nodeName() const final { return name(); } + NodeType nodeType() const final { return ATTRIBUTE_NODE; } - virtual const AtomicString& localName() const override { return m_name.localName(); } - virtual const AtomicString& prefix() const override { return m_name.prefix(); } + String nodeValue() const final { return value(); } + ExceptionOr<void> setNodeValue(const String&) final; - virtual void setPrefix(const AtomicString&, ExceptionCode&) override; + ExceptionOr<void> setPrefix(const AtomicString&) final; - virtual String nodeValue() const override { return value(); } - virtual void setNodeValue(const String&, ExceptionCode&) override; - virtual PassRefPtr<Node> cloneNode(bool deep) override; + Ref<Node> cloneNodeInternal(Document&, CloningOperation) final; - virtual bool isAttributeNode() const override { return true; } - virtual bool childTypeAllowed(NodeType) const override; + bool isAttributeNode() const final { return true; } + bool childTypeAllowed(NodeType) const final; - virtual void childrenChanged(const ChildChange&) override; + void childrenChanged(const ChildChange&) final; Attribute& elementAttribute(); // Attr wraps either an element/name, or a name/value pair (when it's a standalone Node.) // Note that m_name is always set, but m_element/m_standaloneValue may be null. - Element* m_element; + Element* m_element { nullptr }; QualifiedName m_name; AtomicString m_standaloneValue; RefPtr<MutableStyleProperties> m_style; - unsigned m_ignoreChildrenChanged; + unsigned m_ignoreChildrenChanged { 0 }; }; -inline bool isAttr(const Node& node) { return node.isAttributeNode(); } -void isAttr(const Attr&); // Catch unnecessary runtime check of type known at compile time. - -NODE_TYPE_CASTS(Attr) - } // namespace WebCore -#endif // Attr_h +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::Attr) + static bool isType(const WebCore::Node& node) { return node.isAttributeNode(); } +SPECIALIZE_TYPE_TRAITS_END() diff --git a/Source/WebCore/dom/Attr.idl b/Source/WebCore/dom/Attr.idl index 15c001a6b..4cf65336f 100644 --- a/Source/WebCore/dom/Attr.idl +++ b/Source/WebCore/dom/Attr.idl @@ -20,28 +20,18 @@ [ JSCustomMarkFunction, - JSGenerateToNativeObject + JSGenerateToJSObject, + JSGenerateToNativeObject, ] interface Attr : Node { - - // DOM Level 1 - - [TreatReturnedNullStringAs=Null] readonly attribute DOMString name; + readonly attribute DOMString? name; readonly attribute boolean specified; - [TreatReturnedNullStringAs=Null, TreatNullAs=NullString, SetterRaisesException] attribute DOMString value; - - // DOM Level 2 + [CEReactions, ImplementedAs=valueForBindings] attribute DOMString value; readonly attribute Element ownerElement; - // DOM Level 3 - - readonly attribute boolean isId; - -#if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C - // This extension is no longer needed, but it has to remain available in Objective C, as it's public API. - readonly attribute CSSStyleDeclaration style; -#endif + readonly attribute DOMString? namespaceURI; + readonly attribute DOMString? prefix; + readonly attribute DOMString localName; }; - diff --git a/Source/WebCore/dom/Attribute.h b/Source/WebCore/dom/Attribute.h index 61c9ec5a8..72dbed69e 100644 --- a/Source/WebCore/dom/Attribute.h +++ b/Source/WebCore/dom/Attribute.h @@ -3,7 +3,7 @@ * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Peter Kelly (pmk@post.com) * (C) 2001 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2012 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2012, 2014 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -22,8 +22,7 @@ * */ -#ifndef Attribute_h -#define Attribute_h +#pragma once #include "QualifiedName.h" @@ -44,13 +43,16 @@ public: // as the Attribute stays in place. For example, calling a function that mutates // an Element's internal attribute storage may invalidate them. const AtomicString& value() const { return m_value; } + static ptrdiff_t valueMemoryOffset() { return OBJECT_OFFSETOF(Attribute, m_value); } const AtomicString& prefix() const { return m_name.prefix(); } const AtomicString& localName() const { return m_name.localName(); } const AtomicString& namespaceURI() const { return m_name.namespaceURI(); } const QualifiedName& name() const { return m_name; } + static ptrdiff_t nameMemoryOffset() { return OBJECT_OFFSETOF(Attribute, m_name); } bool isEmpty() const { return m_value.isEmpty(); } + static bool nameMatchesFilter(const QualifiedName&, const AtomicString& filterPrefix, const AtomicString& filterLocalName, const AtomicString& filterNamespaceURI); bool matches(const AtomicString& prefix, const AtomicString& localName, const AtomicString& namespaceURI) const; void setValue(const AtomicString& value) { m_value = value; } @@ -72,13 +74,16 @@ private: AtomicString m_value; }; -inline bool Attribute::matches(const AtomicString& prefix, const AtomicString& localName, const AtomicString& namespaceURI) const +inline bool Attribute::nameMatchesFilter(const QualifiedName& name, const AtomicString& filterPrefix, const AtomicString& filterLocalName, const AtomicString& filterNamespaceURI) { - if (localName != this->localName()) + if (filterLocalName != name.localName()) return false; - return prefix == starAtom || namespaceURI == this->namespaceURI(); + return filterPrefix == starAtom || filterNamespaceURI == name.namespaceURI(); } -} // namespace WebCore +inline bool Attribute::matches(const AtomicString& prefix, const AtomicString& localName, const AtomicString& namespaceURI) const +{ + return nameMatchesFilter(m_name, prefix, localName, namespaceURI); +} -#endif // Attribute_h +} // namespace WebCore diff --git a/Source/WebCore/dom/AutocompleteErrorEvent.h b/Source/WebCore/dom/AutocompleteErrorEvent.h new file mode 100644 index 000000000..f5d766836 --- /dev/null +++ b/Source/WebCore/dom/AutocompleteErrorEvent.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2013 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(REQUEST_AUTOCOMPLETE) + +#include "Event.h" +#include "EventNames.h" + +namespace WebCore { + +class AutocompleteErrorEvent final : public Event { +public: + static Ref<AutocompleteErrorEvent> create() + { + return adoptRef(*new AutocompleteErrorEvent); + } + + static Ref<AutocompleteErrorEvent> create(const String& reason) + { + return adoptRef(*new AutocompleteErrorEvent(reason)); + } + + struct Init : EventInit { + String reason; + }; + + static Ref<AutocompleteErrorEvent> create(const AtomicString& eventType, const Init& initializer, IsTrusted isTrusted = IsTrusted::No) + { + return adoptRef(*new AutocompleteErrorEvent(eventType, initializer, isTrusted)); + } + + const String& reason() const { return m_reason; } + +private: + AutocompleteErrorEvent() + { + } + + AutocompleteErrorEvent(const String& reason) + : Event(eventNames().autocompleteerrorEvent, false, false) + , m_reason(reason) + { + } + + AutocompleteErrorEvent(const AtomicString& eventType, const Init& initializer, IsTrusted isTrusted) + : Event(eventType, initializer, isTrusted) + , m_reason(initializer.reason) + { + } + + String m_reason; +}; + +} // namespace WebCore + +#endif // ENABLE(REQUEST_AUTOCOMPLETE) diff --git a/Source/WebCore/dom/AutocompleteErrorEvent.idl b/Source/WebCore/dom/AutocompleteErrorEvent.idl new file mode 100644 index 000000000..d93fa65fb --- /dev/null +++ b/Source/WebCore/dom/AutocompleteErrorEvent.idl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2013 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=REQUEST_AUTOCOMPLETE, + Constructor(DOMString type, optional AutocompleteErrorEventInit eventInitDict), + JSGenerateToJSObject +] interface AutocompleteErrorEvent : Event { + readonly attribute DOMString reason; +}; + +dictionary AutocompleteErrorEventInit : EventInit { + DOMString reason = ""; +}; diff --git a/Source/WebCore/dom/BeforeLoadEvent.h b/Source/WebCore/dom/BeforeLoadEvent.h index 0ae76985c..7c57daaac 100644 --- a/Source/WebCore/dom/BeforeLoadEvent.h +++ b/Source/WebCore/dom/BeforeLoadEvent.h @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY APPLE, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,56 +24,42 @@ * */ -#ifndef BeforeLoadEvent_h -#define BeforeLoadEvent_h +#pragma once #include "Event.h" #include "EventNames.h" namespace WebCore { -struct BeforeLoadEventInit : public EventInit { - BeforeLoadEventInit() - { - }; - - String url; -}; - -class BeforeLoadEvent : public Event { +class BeforeLoadEvent final : public Event { public: - static PassRefPtr<BeforeLoadEvent> create() + static Ref<BeforeLoadEvent> create(const String& url) { - return adoptRef(new BeforeLoadEvent); + return adoptRef(*new BeforeLoadEvent(url)); } - static PassRefPtr<BeforeLoadEvent> create(const String& url) - { - return adoptRef(new BeforeLoadEvent(url)); - } + struct Init : EventInit { + String url; + }; - static PassRefPtr<BeforeLoadEvent> create(const AtomicString& type, const BeforeLoadEventInit& initializer) + static Ref<BeforeLoadEvent> create(const AtomicString& type, const Init& initializer, IsTrusted isTrusted = IsTrusted::No) { - return adoptRef(new BeforeLoadEvent(type, initializer)); + return adoptRef(*new BeforeLoadEvent(type, initializer, isTrusted)); } const String& url() const { return m_url; } - virtual EventInterface eventInterface() const override { return BeforeLoadEventInterfaceType; } + EventInterface eventInterface() const override { return BeforeLoadEventInterfaceType; } private: - BeforeLoadEvent() - { - } - explicit BeforeLoadEvent(const String& url) : Event(eventNames().beforeloadEvent, false, true) , m_url(url) { } - BeforeLoadEvent(const AtomicString& type, const BeforeLoadEventInit& initializer) - : Event(type, initializer) + BeforeLoadEvent(const AtomicString& type, const Init& initializer, IsTrusted isTrusted) + : Event(type, initializer, isTrusted) , m_url(initializer.url) { } @@ -82,5 +68,3 @@ private: }; } // namespace WebCore - -#endif // BeforeLoadEvent_h diff --git a/Source/WebCore/dom/BeforeLoadEvent.idl b/Source/WebCore/dom/BeforeLoadEvent.idl index 54b850199..5dd04ce0d 100644 --- a/Source/WebCore/dom/BeforeLoadEvent.idl +++ b/Source/WebCore/dom/BeforeLoadEvent.idl @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY APPLE, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -25,8 +25,11 @@ */ [ - ConstructorTemplate=Event + Constructor(DOMString type, optional BeforeLoadEventInit eventInitDict) ] interface BeforeLoadEvent : Event { - [InitializedByEventConstructor] readonly attribute DOMString url; + readonly attribute DOMString url; }; +dictionary BeforeLoadEventInit : EventInit { + DOMString url = ""; +}; diff --git a/Source/WebCore/dom/BeforeTextInsertedEvent.cpp b/Source/WebCore/dom/BeforeTextInsertedEvent.cpp index 0db1e2803..86ff9e023 100644 --- a/Source/WebCore/dom/BeforeTextInsertedEvent.cpp +++ b/Source/WebCore/dom/BeforeTextInsertedEvent.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2005 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR diff --git a/Source/WebCore/dom/BeforeTextInsertedEvent.h b/Source/WebCore/dom/BeforeTextInsertedEvent.h index 6e5f4eb79..cc92dfcd2 100644 --- a/Source/WebCore/dom/BeforeTextInsertedEvent.h +++ b/Source/WebCore/dom/BeforeTextInsertedEvent.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -23,34 +23,33 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef BeforeTextInsertedEvent_h -#define BeforeTextInsertedEvent_h +#pragma once #include "Event.h" namespace WebCore { -class BeforeTextInsertedEvent : public Event { +class BeforeTextInsertedEvent final : public Event { public: virtual ~BeforeTextInsertedEvent(); - static PassRefPtr<BeforeTextInsertedEvent> create(const String& text) + static Ref<BeforeTextInsertedEvent> create(const String& text) { - return adoptRef(new BeforeTextInsertedEvent(text)); + return adoptRef(*new BeforeTextInsertedEvent(text)); } - virtual EventInterface eventInterface() const override; - virtual bool isBeforeTextInsertedEvent() const override { return true; } + EventInterface eventInterface() const override; const String& text() const { return m_text; } void setText(const String& s) { m_text = s; } private: explicit BeforeTextInsertedEvent(const String&); + bool isBeforeTextInsertedEvent() const override { return true; } String m_text; }; -} // namespace +} // namespace WebCore -#endif +SPECIALIZE_TYPE_TRAITS_EVENT(BeforeTextInsertedEvent) diff --git a/Source/WebCore/dom/BeforeUnloadEvent.cpp b/Source/WebCore/dom/BeforeUnloadEvent.cpp index d0a999657..25d81fd6b 100644 --- a/Source/WebCore/dom/BeforeUnloadEvent.cpp +++ b/Source/WebCore/dom/BeforeUnloadEvent.cpp @@ -2,7 +2,7 @@ * Copyright (C) 2001 Peter Kelly (pmk@post.com) * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - * Copyright (C) 2003, 2005, 2006 Apple Computer, Inc. + * Copyright (C) 2003, 2005, 2006 Apple Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public diff --git a/Source/WebCore/dom/BeforeUnloadEvent.h b/Source/WebCore/dom/BeforeUnloadEvent.h index 9188ad576..3e726ad92 100644 --- a/Source/WebCore/dom/BeforeUnloadEvent.h +++ b/Source/WebCore/dom/BeforeUnloadEvent.h @@ -2,7 +2,7 @@ * Copyright (C) 2001 Peter Kelly (pmk@post.com) * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * Copyright (C) 2003, 2004, 2005, 2006 Apple Inc. * Copyright (C) 2013 Samsung Electronics * * This library is free software; you can redistribute it and/or @@ -22,8 +22,7 @@ * */ -#ifndef BeforeUnloadEvent_h -#define BeforeUnloadEvent_h +#pragma once #include "Event.h" @@ -33,30 +32,24 @@ class BeforeUnloadEvent final : public Event { public: virtual ~BeforeUnloadEvent(); - static PassRefPtr<BeforeUnloadEvent> create() + static Ref<BeforeUnloadEvent> create() { - return adoptRef(new BeforeUnloadEvent); + return adoptRef(*new BeforeUnloadEvent); } String returnValue() const { return m_returnValue; } void setReturnValue(const String& returnValue) { m_returnValue = returnValue; } - virtual EventInterface eventInterface() const override { return BeforeUnloadEventInterfaceType; } + EventInterface eventInterface() const override { return BeforeUnloadEventInterfaceType; } private: BeforeUnloadEvent(); - virtual bool isBeforeUnloadEvent() const override; + bool isBeforeUnloadEvent() const override; String m_returnValue; }; -inline BeforeUnloadEvent* toBeforeUnloadEvent(Event* event) -{ - ASSERT_WITH_SECURITY_IMPLICATION(!event || event->isBeforeUnloadEvent()); - return static_cast<BeforeUnloadEvent*>(event); -} - } // namespace WebCore -#endif // BeforeUnloadEvent_h +SPECIALIZE_TYPE_TRAITS_EVENT(BeforeUnloadEvent) diff --git a/Source/WebCore/dom/BeforeUnloadEvent.idl b/Source/WebCore/dom/BeforeUnloadEvent.idl index 3e962ef44..c815dfefc 100644 --- a/Source/WebCore/dom/BeforeUnloadEvent.idl +++ b/Source/WebCore/dom/BeforeUnloadEvent.idl @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY APPLE, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR diff --git a/Source/WebCore/dom/CDATASection.cpp b/Source/WebCore/dom/CDATASection.cpp index 4b3972aab..598530f7c 100644 --- a/Source/WebCore/dom/CDATASection.cpp +++ b/Source/WebCore/dom/CDATASection.cpp @@ -31,14 +31,14 @@ inline CDATASection::CDATASection(Document& document, const String& data) { } -PassRefPtr<CDATASection> CDATASection::create(Document& document, const String& data) +Ref<CDATASection> CDATASection::create(Document& document, const String& data) { - return adoptRef(new CDATASection(document, data)); + return adoptRef(*new CDATASection(document, data)); } String CDATASection::nodeName() const { - return "#cdata-section"; + return ASCIILiteral("#cdata-section"); } Node::NodeType CDATASection::nodeType() const @@ -46,9 +46,9 @@ Node::NodeType CDATASection::nodeType() const return CDATA_SECTION_NODE; } -PassRefPtr<Node> CDATASection::cloneNode(bool /*deep*/) +Ref<Node> CDATASection::cloneNodeInternal(Document& targetDocument, CloningOperation) { - return create(document(), data()); + return create(targetDocument, data()); } bool CDATASection::childTypeAllowed(NodeType) const @@ -56,7 +56,7 @@ bool CDATASection::childTypeAllowed(NodeType) const return false; } -PassRefPtr<Text> CDATASection::virtualCreate(const String& data) +Ref<Text> CDATASection::virtualCreate(const String& data) { return create(document(), data); } diff --git a/Source/WebCore/dom/CDATASection.h b/Source/WebCore/dom/CDATASection.h index a93dc496c..bdb9db7b3 100644 --- a/Source/WebCore/dom/CDATASection.h +++ b/Source/WebCore/dom/CDATASection.h @@ -20,8 +20,7 @@ * */ -#ifndef CDATASection_h -#define CDATASection_h +#pragma once #include "Text.h" @@ -29,24 +28,20 @@ namespace WebCore { class CDATASection final : public Text { public: - static PassRefPtr<CDATASection> create(Document&, const String&); + static Ref<CDATASection> create(Document&, const String&); private: CDATASection(Document&, const String&); - virtual String nodeName() const override; - virtual NodeType nodeType() const override; - virtual PassRefPtr<Node> cloneNode(bool deep) override; - virtual bool childTypeAllowed(NodeType) const override; - virtual PassRefPtr<Text> virtualCreate(const String&) override; + String nodeName() const override; + NodeType nodeType() const override; + Ref<Node> cloneNodeInternal(Document&, CloningOperation) override; + bool childTypeAllowed(NodeType) const override; + Ref<Text> virtualCreate(const String&) override; }; -inline bool isCDATASection(const Node& node) { return node.nodeType() == Node::CDATA_SECTION_NODE; } -void isCDATASection(const CDATASection&); // Catch unnecessary runtime check of type known at compile time. -void isCDATASection(const ContainerNode&); // Catch unnecessary runtime check of type known at compile time. - -NODE_TYPE_CASTS(CDATASection) - } // namespace WebCore -#endif // CDATASection_h +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::CDATASection) + static bool isType(const WebCore::Node& node) { return node.nodeType() == WebCore::Node::CDATA_SECTION_NODE; } +SPECIALIZE_TYPE_TRAITS_END() diff --git a/Source/WebCore/dom/CDATASection.idl b/Source/WebCore/dom/CDATASection.idl index 939fe27b3..fc7a8ae47 100644 --- a/Source/WebCore/dom/CDATASection.idl +++ b/Source/WebCore/dom/CDATASection.idl @@ -17,6 +17,8 @@ * Boston, MA 02110-1301, USA. */ -interface CDATASection : Text { +[ + JSGenerateToJSObject +] interface CDATASection : Text { }; diff --git a/Source/WebCore/dom/CharacterData.cpp b/Source/WebCore/dom/CharacterData.cpp index 7ac273f62..4e564bce2 100644 --- a/Source/WebCore/dom/CharacterData.cpp +++ b/Source/WebCore/dom/CharacterData.cpp @@ -22,40 +22,53 @@ #include "config.h" #include "CharacterData.h" +#include "Attr.h" #include "ElementTraversal.h" +#include "EventNames.h" #include "ExceptionCode.h" #include "FrameSelection.h" #include "InspectorInstrumentation.h" #include "MutationEvent.h" #include "MutationObserverInterestGroup.h" #include "MutationRecord.h" +#include "NoEventDispatchAssertion.h" #include "ProcessingInstruction.h" #include "RenderText.h" #include "StyleInheritedData.h" -#include "TextBreakIterator.h" +#include <unicode/ubrk.h> #include <wtf/Ref.h> namespace WebCore { -void CharacterData::setData(const String& data, ExceptionCode&) +static bool canUseSetDataOptimization(const CharacterData& node) +{ + auto& document = node.document(); + return !document.hasListenerType(Document::DOMCHARACTERDATAMODIFIED_LISTENER) && !document.hasMutationObserversOfType(MutationObserver::CharacterData) + && !document.hasListenerType(Document::DOMSUBTREEMODIFIED_LISTENER); +} + +void CharacterData::setData(const String& data) { const String& nonNullData = !data.isNull() ? data : emptyString(); - if (m_data == nonNullData) - return; + unsigned oldLength = length(); - Ref<CharacterData> protect(*this); + if (m_data == nonNullData && canUseSetDataOptimization(*this)) { + document().textRemoved(this, 0, oldLength); + if (document().frame()) + document().frame()->selection().textWasReplaced(this, 0, oldLength, oldLength); + return; + } - unsigned oldLength = length(); + Ref<CharacterData> protectedThis(*this); setDataAndUpdate(nonNullData, 0, oldLength, nonNullData.length()); document().textRemoved(this, 0, oldLength); } -String CharacterData::substringData(unsigned offset, unsigned count, ExceptionCode& ec) +ExceptionOr<String> CharacterData::substringData(unsigned offset, unsigned count) { - checkCharDataOperation(offset, ec); - if (ec) - return String(); + if (offset > length()) + return Exception { INDEX_SIZE_ERR }; return m_data.substring(offset, count); } @@ -75,8 +88,8 @@ unsigned CharacterData::parserAppendData(const String& string, unsigned offset, // We need at least two characters look-ahead to account for UTF-16 surrogates. if (characterLengthLimit < characterLength) { NonSharedCharacterBreakIterator it(StringView(string).substring(offset, (characterLengthLimit + 2 > characterLength) ? characterLength : characterLengthLimit + 2)); - if (!isTextBreak(it, characterLengthLimit)) - characterLengthLimit = textBreakPreceding(it, characterLengthLimit); + if (!ubrk_isBoundary(it, characterLengthLimit)) + characterLengthLimit = ubrk_preceding(it, characterLengthLimit); } if (!characterLengthLimit) @@ -87,27 +100,16 @@ unsigned CharacterData::parserAppendData(const String& string, unsigned offset, else m_data.append(string.characters16() + offset, characterLengthLimit); - ASSERT(!renderer() || isTextNode()); - if (isTextNode()) - Style::updateTextRendererAfterContentChange(*toText(this), oldLength, 0); + ASSERT(!renderer() || is<Text>(*this)); + if (is<Text>(*this) && parentNode()) + downcast<Text>(*this).updateRendererAfterContentChange(oldLength, 0); - document().incDOMTreeVersion(); - // We don't call dispatchModifiedEvent here because we don't want the - // parser to dispatch DOM mutation events. - if (parentNode()) { - ContainerNode::ChildChange change = { - ContainerNode::TextChanged, - ElementTraversal::previousSibling(this), - ElementTraversal::nextSibling(this), - ContainerNode::ChildChangeSourceParser - }; - parentNode()->childrenChanged(change); - } + notifyParentAfterChange(ContainerNode::ChildChangeSourceParser); return characterLengthLimit; } -void CharacterData::appendData(const String& data, ExceptionCode&) +void CharacterData::appendData(const String& data) { String newStr = m_data; newStr.append(data); @@ -117,11 +119,10 @@ void CharacterData::appendData(const String& data, ExceptionCode&) // FIXME: Should we call textInserted here? } -void CharacterData::insertData(unsigned offset, const String& data, ExceptionCode& ec) +ExceptionOr<void> CharacterData::insertData(unsigned offset, const String& data) { - checkCharDataOperation(offset, ec); - if (ec) - return; + if (offset > length()) + return Exception { INDEX_SIZE_ERR }; String newStr = m_data; newStr.insert(data, offset); @@ -129,49 +130,45 @@ void CharacterData::insertData(unsigned offset, const String& data, ExceptionCod setDataAndUpdate(newStr, offset, 0, data.length()); document().textInserted(this, offset, data.length()); + + return { }; } -void CharacterData::deleteData(unsigned offset, unsigned count, ExceptionCode& ec) +ExceptionOr<void> CharacterData::deleteData(unsigned offset, unsigned count) { - checkCharDataOperation(offset, ec); - if (ec) - return; + if (offset > length()) + return Exception { INDEX_SIZE_ERR }; - unsigned realCount; - if (offset + count > length()) - realCount = length() - offset; - else - realCount = count; + count = std::min(count, length() - offset); String newStr = m_data; - newStr.remove(offset, realCount); + newStr.remove(offset, count); setDataAndUpdate(newStr, offset, count, 0); - document().textRemoved(this, offset, realCount); + document().textRemoved(this, offset, count); + + return { }; } -void CharacterData::replaceData(unsigned offset, unsigned count, const String& data, ExceptionCode& ec) +ExceptionOr<void> CharacterData::replaceData(unsigned offset, unsigned count, const String& data) { - checkCharDataOperation(offset, ec); - if (ec) - return; + if (offset > length()) + return Exception { INDEX_SIZE_ERR }; - unsigned realCount; - if (offset + count > length()) - realCount = length() - offset; - else - realCount = count; + count = std::min(count, length() - offset); String newStr = m_data; - newStr.remove(offset, realCount); + newStr.remove(offset, count); newStr.insert(data, offset); setDataAndUpdate(newStr, offset, count, data.length()); // update the markers for spell checking and grammar checking - document().textRemoved(this, offset, realCount); + document().textRemoved(this, offset, count); document().textInserted(this, offset, data.length()); + + return { }; } String CharacterData::nodeValue() const @@ -184,9 +181,10 @@ bool CharacterData::containsOnlyWhitespace() const return m_data.containsOnlyWhitespace(); } -void CharacterData::setNodeValue(const String& nodeValue, ExceptionCode& ec) +ExceptionOr<void> CharacterData::setNodeValue(const String& nodeValue) { - setData(nodeValue, ec); + setData(nodeValue); + return { }; } void CharacterData::setDataAndUpdate(const String& newData, unsigned offsetOfReplacedData, unsigned oldLength, unsigned newLength) @@ -194,54 +192,60 @@ void CharacterData::setDataAndUpdate(const String& newData, unsigned offsetOfRep String oldData = m_data; m_data = newData; - ASSERT(!renderer() || isTextNode()); - if (isTextNode()) - Style::updateTextRendererAfterContentChange(*toText(this), offsetOfReplacedData, oldLength); + ASSERT(!renderer() || is<Text>(*this)); + if (is<Text>(*this) && parentNode()) + downcast<Text>(*this).updateRendererAfterContentChange(offsetOfReplacedData, oldLength); - if (nodeType() == PROCESSING_INSTRUCTION_NODE) - toProcessingInstruction(this)->checkStyleSheet(); + if (is<ProcessingInstruction>(*this)) + downcast<ProcessingInstruction>(*this).checkStyleSheet(); if (document().frame()) document().frame()->selection().textWasReplaced(this, offsetOfReplacedData, oldLength, newLength); - document().incDOMTreeVersion(); + notifyParentAfterChange(ContainerNode::ChildChangeSourceAPI); + dispatchModifiedEvent(oldData); } +void CharacterData::notifyParentAfterChange(ContainerNode::ChildChangeSource source) +{ +#if !ASSERT_DISABLED + auto assertNoEventDispatch = std::make_unique<NoEventDispatchAssertion>(); +#endif + + document().incDOMTreeVersion(); + + if (!parentNode()) + return; + + ContainerNode::ChildChange change = { + ContainerNode::TextChanged, + ElementTraversal::previousSibling(*this), + ElementTraversal::nextSibling(*this), + source + }; + +#if !ASSERT_DISABLED + // Attribute CharacterData is expected to fire events. + if (is<Attr>(*parentNode())) + assertNoEventDispatch = nullptr; +#endif + + parentNode()->childrenChanged(change); +} + void CharacterData::dispatchModifiedEvent(const String& oldData) { - if (OwnPtr<MutationObserverInterestGroup> mutationRecipients = MutationObserverInterestGroup::createForCharacterDataMutation(*this)) + if (auto mutationRecipients = MutationObserverInterestGroup::createForCharacterDataMutation(*this)) mutationRecipients->enqueueMutationRecord(MutationRecord::createCharacterData(*this, oldData)); if (!isInShadowTree()) { - if (parentNode()) { - ContainerNode::ChildChange change = { - ContainerNode::TextChanged, - ElementTraversal::previousSibling(this), - ElementTraversal::nextSibling(this), - ContainerNode::ChildChangeSourceAPI - }; - parentNode()->childrenChanged(change); - } if (document().hasListenerType(Document::DOMCHARACTERDATAMODIFIED_LISTENER)) - dispatchScopedEvent(MutationEvent::create(eventNames().DOMCharacterDataModifiedEvent, true, 0, oldData, m_data)); + dispatchScopedEvent(MutationEvent::create(eventNames().DOMCharacterDataModifiedEvent, true, nullptr, oldData, m_data)); dispatchSubtreeModifiedEvent(); } -#if ENABLE(INSPECTOR) - InspectorInstrumentation::characterDataModified(&document(), this); -#endif -} -void CharacterData::checkCharDataOperation(unsigned offset, ExceptionCode& ec) -{ - ec = 0; - - // INDEX_SIZE_ERR: Raised if the specified offset is negative or greater than the number of 16-bit - // units in data. - if (offset > length()) { - ec = INDEX_SIZE_ERR; - return; - } + InspectorInstrumentation::characterDataModified(document(), *this); } int CharacterData::maxCharacterOffset() const diff --git a/Source/WebCore/dom/CharacterData.h b/Source/WebCore/dom/CharacterData.h index 681c63b99..e4ad221ed 100644 --- a/Source/WebCore/dom/CharacterData.h +++ b/Source/WebCore/dom/CharacterData.h @@ -20,36 +20,34 @@ * */ -#ifndef CharacterData_h -#define CharacterData_h +#pragma once -#include "Node.h" -#include <wtf/text/WTFString.h> +#include "ContainerNode.h" namespace WebCore { class CharacterData : public Node { public: - String data() const { return m_data; } - void setData(const String&, ExceptionCode&); + const String& data() const { return m_data; } + static ptrdiff_t dataMemoryOffset() { return OBJECT_OFFSETOF(CharacterData, m_data); } + + WEBCORE_EXPORT void setData(const String&); unsigned length() const { return m_data.length(); } - String substringData(unsigned offset, unsigned count, ExceptionCode&); - void appendData(const String&, ExceptionCode&); - void insertData(unsigned offset, const String&, ExceptionCode&); - void deleteData(unsigned offset, unsigned count, ExceptionCode&); - void replaceData(unsigned offset, unsigned count, const String&, ExceptionCode&); + WEBCORE_EXPORT ExceptionOr<String> substringData(unsigned offset, unsigned count); + WEBCORE_EXPORT void appendData(const String&); + WEBCORE_EXPORT ExceptionOr<void> insertData(unsigned offset, const String&); + WEBCORE_EXPORT ExceptionOr<void> deleteData(unsigned offset, unsigned count); + WEBCORE_EXPORT ExceptionOr<void> replaceData(unsigned offset, unsigned count, const String&); bool containsOnlyWhitespace() const; - StringImpl* dataImpl() { return m_data.impl(); } - // Like appendData, but optimized for the parser (e.g., no mutation events). // Returns how much could be added before length limit was met. unsigned parserAppendData(const String& string, unsigned offset, unsigned lengthLimit); protected: CharacterData(Document& document, const String& text, ConstructionType type) - : Node(&document, type) + : Node(document, type) , m_data(!text.isNull() ? text : emptyString()) { ASSERT(type == CreateOther || type == CreateText || type == CreateEditingText); @@ -63,23 +61,19 @@ protected: void dispatchModifiedEvent(const String& oldValue); private: - virtual String nodeValue() const override final; - virtual void setNodeValue(const String&, ExceptionCode&) override final; - virtual bool isCharacterDataNode() const override final { return true; } - virtual int maxCharacterOffset() const override final; - virtual bool offsetInCharacters() const override final; + String nodeValue() const final; + ExceptionOr<void> setNodeValue(const String&) final; + bool isCharacterDataNode() const final { return true; } + int maxCharacterOffset() const final; + bool offsetInCharacters() const final; void setDataAndUpdate(const String&, unsigned offsetOfReplacedData, unsigned oldLength, unsigned newLength); - void checkCharDataOperation(unsigned offset, ExceptionCode&); + void notifyParentAfterChange(ContainerNode::ChildChangeSource); String m_data; }; -inline bool isCharacterData(const Node& node) { return node.isCharacterDataNode(); } -void isCharacterData(const CharacterData&); // Catch unnecessary runtime check of type known at compile time. -void isCharacterData(const ContainerNode&); // Catch unnecessary runtime check of type known at compile time. - -NODE_TYPE_CASTS(CharacterData) - } // namespace WebCore -#endif // CharacterData_h +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::CharacterData) + static bool isType(const WebCore::Node& node) { return node.isCharacterDataNode(); } +SPECIALIZE_TYPE_TRAITS_END() diff --git a/Source/WebCore/dom/CharacterData.idl b/Source/WebCore/dom/CharacterData.idl index c8c972319..366081b41 100644 --- a/Source/WebCore/dom/CharacterData.idl +++ b/Source/WebCore/dom/CharacterData.idl @@ -18,26 +18,16 @@ */ interface CharacterData : Node { - - [TreatNullAs=NullString, SetterRaisesException] attribute DOMString data; - + [CEReactions, TreatNullAs=EmptyString] attribute DOMString data; readonly attribute unsigned long length; - - [TreatReturnedNullStringAs=Null, ObjCLegacyUnnamedParameters, RaisesException] DOMString substringData([IsIndex, Default=Undefined] optional unsigned long offset, - [IsIndex, Default=Undefined] optional unsigned long length); - - [RaisesException] void appendData([Default=Undefined] optional DOMString data); - [ObjCLegacyUnnamedParameters, RaisesException] void insertData([IsIndex, Default=Undefined] optional unsigned long offset, - [Default=Undefined] optional DOMString data); + [MayThrowException] DOMString? substringData(unsigned long offset, unsigned long length); - [ObjCLegacyUnnamedParameters, RaisesException] void deleteData([IsIndex, Default=Undefined] optional unsigned long offset, - [IsIndex, Default=Undefined] optional unsigned long length); - - [ObjCLegacyUnnamedParameters, RaisesException] void replaceData([IsIndex, Default=Undefined] optional unsigned long offset, - [IsIndex, Default=Undefined] optional unsigned long length, - [Default=Undefined] optional DOMString data); + [CEReactions] void appendData(DOMString data); + [CEReactions, MayThrowException] void insertData(unsigned long offset, DOMString data); + [CEReactions, MayThrowException] void deleteData(unsigned long offset, unsigned long length); + [CEReactions, MayThrowException] void replaceData(unsigned long offset, unsigned long length, DOMString data); }; CharacterData implements ChildNode; - +CharacterData implements NonDocumentTypeChildNode; diff --git a/Source/WebCore/dom/ChildListMutationScope.cpp b/Source/WebCore/dom/ChildListMutationScope.cpp index 2c5071649..0fee84cd7 100644 --- a/Source/WebCore/dom/ChildListMutationScope.cpp +++ b/Source/WebCore/dom/ChildListMutationScope.cpp @@ -29,13 +29,12 @@ */ #include "config.h" - #include "ChildListMutationScope.h" -#include "DocumentFragment.h" #include "MutationObserverInterestGroup.h" #include "MutationRecord.h" #include "StaticNodeList.h" +#include <wtf/NeverDestroyed.h> #include <wtf/StdLibExtras.h> namespace WebCore { @@ -43,14 +42,14 @@ namespace WebCore { typedef HashMap<ContainerNode*, ChildListMutationAccumulator*> AccumulatorMap; static AccumulatorMap& accumulatorMap() { - DEFINE_STATIC_LOCAL(AccumulatorMap, map, ()); + static NeverDestroyed<AccumulatorMap> map; return map; } -ChildListMutationAccumulator::ChildListMutationAccumulator(ContainerNode& target, PassOwnPtr<MutationObserverInterestGroup> observers) +ChildListMutationAccumulator::ChildListMutationAccumulator(ContainerNode& target, std::unique_ptr<MutationObserverInterestGroup> observers) : m_target(target) - , m_lastAdded(0) - , m_observers(observers) + , m_lastAdded(nullptr) + , m_observers(WTFMove(observers)) { } @@ -58,10 +57,10 @@ ChildListMutationAccumulator::~ChildListMutationAccumulator() { if (!isEmpty()) enqueueMutationRecord(); - accumulatorMap().remove(&m_target.get()); + accumulatorMap().remove(m_target.ptr()); } -PassRefPtr<ChildListMutationAccumulator> ChildListMutationAccumulator::getOrCreate(ContainerNode& target) +RefPtr<ChildListMutationAccumulator> ChildListMutationAccumulator::getOrCreate(ContainerNode& target) { AccumulatorMap::AddResult result = accumulatorMap().add(&target, nullptr); RefPtr<ChildListMutationAccumulator> accumulator; @@ -71,7 +70,7 @@ PassRefPtr<ChildListMutationAccumulator> ChildListMutationAccumulator::getOrCrea accumulator = adoptRef(new ChildListMutationAccumulator(target, MutationObserverInterestGroup::createForChildListMutation(target))); result.iterator->value = accumulator.get(); } - return accumulator.release(); + return accumulator; } inline bool ChildListMutationAccumulator::isAddedNodeInOrder(Node& child) @@ -85,7 +84,7 @@ void ChildListMutationAccumulator::childAdded(Node& childRef) Ref<Node> child(childRef); - if (!isAddedNodeInOrder(child.get())) + if (!isAddedNodeInOrder(child)) enqueueMutationRecord(); if (isEmpty()) { @@ -93,7 +92,7 @@ void ChildListMutationAccumulator::childAdded(Node& childRef) m_nextSibling = child->nextSibling(); } - m_lastAdded = &child.get(); + m_lastAdded = child.ptr(); m_addedNodes.append(child.get()); } @@ -108,7 +107,7 @@ void ChildListMutationAccumulator::willRemoveChild(Node& childRef) Ref<Node> child(childRef); - if (!m_addedNodes.isEmpty() || !isRemovedNodeInOrder(child.get())) + if (!m_addedNodes.isEmpty() || !isRemovedNodeInOrder(child)) enqueueMutationRecord(); if (isEmpty()) { @@ -126,11 +125,9 @@ void ChildListMutationAccumulator::enqueueMutationRecord() ASSERT(hasObservers()); ASSERT(!isEmpty()); - RefPtr<NodeList> addedNodes = StaticNodeList::adopt(m_addedNodes); - RefPtr<NodeList> removedNodes = StaticNodeList::adopt(m_removedNodes); - RefPtr<MutationRecord> record = MutationRecord::createChildList(m_target.get(), addedNodes.release(), removedNodes.release(), m_previousSibling.release(), m_nextSibling.release()); - m_observers->enqueueMutationRecord(record.release()); - m_lastAdded = 0; + auto record = MutationRecord::createChildList(m_target, StaticNodeList::create(WTFMove(m_addedNodes)), StaticNodeList::create(WTFMove(m_removedNodes)), WTFMove(m_previousSibling), WTFMove(m_nextSibling)); + m_observers->enqueueMutationRecord(WTFMove(record)); + m_lastAdded = nullptr; ASSERT(isEmpty()); } diff --git a/Source/WebCore/dom/ChildListMutationScope.h b/Source/WebCore/dom/ChildListMutationScope.h index a179f59b7..945aad5e3 100644 --- a/Source/WebCore/dom/ChildListMutationScope.h +++ b/Source/WebCore/dom/ChildListMutationScope.h @@ -28,15 +28,13 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ChildListMutationScope_h -#define ChildListMutationScope_h +#pragma once #include "Document.h" #include "MutationObserver.h" #include "Node.h" -#include <wtf/HashMap.h> +#include <memory> #include <wtf/Noncopyable.h> -#include <wtf/OwnPtr.h> #include <wtf/RefCounted.h> namespace WebCore { @@ -46,16 +44,16 @@ class MutationObserverInterestGroup; // ChildListMutationAccumulator is not meant to be used directly; ChildListMutationScope is the public interface. class ChildListMutationAccumulator : public RefCounted<ChildListMutationAccumulator> { public: - static PassRefPtr<ChildListMutationAccumulator> getOrCreate(ContainerNode&); + static RefPtr<ChildListMutationAccumulator> getOrCreate(ContainerNode&); ~ChildListMutationAccumulator(); void childAdded(Node&); void willRemoveChild(Node&); - bool hasObservers() const { return m_observers; } + bool hasObservers() const { return !!m_observers; } private: - ChildListMutationAccumulator(ContainerNode&, PassOwnPtr<MutationObserverInterestGroup>); + ChildListMutationAccumulator(ContainerNode&, std::unique_ptr<MutationObserverInterestGroup>); void enqueueMutationRecord(); bool isEmpty(); @@ -70,7 +68,7 @@ private: RefPtr<Node> m_nextSibling; Node* m_lastAdded; - OwnPtr<MutationObserverInterestGroup> m_observers; + std::unique_ptr<MutationObserverInterestGroup> m_observers; }; class ChildListMutationScope { @@ -82,6 +80,8 @@ public: m_accumulator = ChildListMutationAccumulator::getOrCreate(target); } + bool canObserve() const { return m_accumulator; } + void childAdded(Node& child) { if (m_accumulator && m_accumulator->hasObservers()) @@ -99,5 +99,3 @@ private: }; } // namespace WebCore - -#endif // ChildListMutationScope_h diff --git a/Source/WebCore/dom/ChildNode.idl b/Source/WebCore/dom/ChildNode.idl index a45bcf484..54f70a341 100644 --- a/Source/WebCore/dom/ChildNode.idl +++ b/Source/WebCore/dom/ChildNode.idl @@ -18,16 +18,12 @@ * Boston, MA 02110-1301, USA. */ -// DOM 4 +// https://dom.spec.whatwg.org/#childnode [ NoInterfaceObject, ] interface ChildNode { - // readonly attribute Element? previousElementSibling; - // readonly attribute Element? nextElementSibling; - - // void before((Node or DOMString)... nodes); - // void after((Node or DOMString)... nodes); - // void replace((Node or DOMString)... nodes); - [RaisesException] void remove(); + [CEReactions, Unscopable, MayThrowException] void before((Node or DOMString)... nodes); + [CEReactions, Unscopable, MayThrowException] void after((Node or DOMString)... nodes); + [CEReactions, Unscopable, MayThrowException] void replaceWith((Node or DOMString)... nodes); + [CEReactions, Unscopable, MayThrowException] void remove(); }; - diff --git a/Source/WebCore/dom/ChildNodeList.cpp b/Source/WebCore/dom/ChildNodeList.cpp index c8fff4e03..95baa2f5f 100644 --- a/Source/WebCore/dom/ChildNodeList.cpp +++ b/Source/WebCore/dom/ChildNodeList.cpp @@ -35,6 +35,7 @@ EmptyNodeList::~EmptyNodeList() ChildNodeList::ChildNodeList(ContainerNode& parent) : m_parent(parent) + , m_indexCache(*this) { } @@ -53,7 +54,7 @@ Node* ChildNodeList::item(unsigned index) const return m_indexCache.nodeAt(*this, index); } -Node* ChildNodeList::collectionFirst() const +Node* ChildNodeList::collectionBegin() const { return m_parent->firstChild(); } @@ -63,47 +64,26 @@ Node* ChildNodeList::collectionLast() const return m_parent->lastChild(); } -Node* ChildNodeList::collectionTraverseForward(Node& current, unsigned count, unsigned& traversedCount) const +void ChildNodeList::collectionTraverseForward(Node*& current, unsigned count, unsigned& traversedCount) const { ASSERT(count); - Node* child = ¤t; for (traversedCount = 0; traversedCount < count; ++traversedCount) { - child = child->nextSibling(); - if (!child) - return nullptr; + current = current->nextSibling(); + if (!current) + return; } - return child; } -Node* ChildNodeList::collectionTraverseBackward(Node& current, unsigned count) const +void ChildNodeList::collectionTraverseBackward(Node*& current, unsigned count) const { ASSERT(count); - Node* child = ¤t; - for (; count && child ; --count) - child = child->previousSibling(); - return child; -} - -Node* ChildNodeList::namedItem(const AtomicString& name) const -{ - // FIXME: According to the spec child node list should not have namedItem(). - if (m_parent.get().inDocument()) { - Element* element = m_parent.get().treeScope().getElementById(name); - if (element && element->parentNode() == &m_parent.get()) - return element; - if (!element || !m_parent.get().treeScope().containsMultipleElementsWithId(name)) - return nullptr; - } - for (auto& element : childrenOfType<Element>(m_parent.get())) { - if (element.hasID() && element.idForStyleResolution() == name) - return const_cast<Element*>(&element); - } - return nullptr; + for (; count && current ; --count) + current = current->previousSibling(); } void ChildNodeList::invalidateCache() { - m_indexCache.invalidate(); + m_indexCache.invalidate(*this); } } // namespace WebCore diff --git a/Source/WebCore/dom/ChildNodeList.h b/Source/WebCore/dom/ChildNodeList.h index 2454e8e0c..818227aa8 100644 --- a/Source/WebCore/dom/ChildNodeList.h +++ b/Source/WebCore/dom/ChildNodeList.h @@ -21,13 +21,11 @@ * */ -#ifndef ChildNodeList_h -#define ChildNodeList_h +#pragma once #include "CollectionIndexCache.h" #include "NodeList.h" #include <wtf/Ref.h> -#include <wtf/RefPtr.h> namespace WebCore { @@ -35,59 +33,59 @@ class ContainerNode; class EmptyNodeList final : public NodeList { public: - static PassRefPtr<EmptyNodeList> create(Node& owner) + static Ref<EmptyNodeList> create(Node& owner) { - return adoptRef(new EmptyNodeList(owner)); + return adoptRef(*new EmptyNodeList(owner)); } virtual ~EmptyNodeList(); - Node& ownerNode() { return m_owner.get(); } + Node& ownerNode() { return m_owner; } private: explicit EmptyNodeList(Node& owner) : m_owner(owner) { } - virtual unsigned length() const override { return 0; } - virtual Node* item(unsigned) const override { return nullptr; } - virtual Node* namedItem(const AtomicString&) const override { return nullptr; } + unsigned length() const override { return 0; } + Node* item(unsigned) const override { return nullptr; } + size_t memoryCost() const override { return 0; } - virtual bool isEmptyNodeList() const override { return true; } + bool isEmptyNodeList() const override { return true; } Ref<Node> m_owner; }; class ChildNodeList final : public NodeList { public: - static PassRefPtr<ChildNodeList> create(ContainerNode& parent) + static Ref<ChildNodeList> create(ContainerNode& parent) { - return adoptRef(new ChildNodeList(parent)); + return adoptRef(*new ChildNodeList(parent)); } virtual ~ChildNodeList(); - ContainerNode& ownerNode() { return m_parent.get(); } + ContainerNode& ownerNode() { return m_parent; } void invalidateCache(); // For CollectionIndexCache - Node* collectionFirst() const; + Node* collectionBegin() const; Node* collectionLast() const; - Node* collectionTraverseForward(Node&, unsigned count, unsigned& traversedCount) const; - Node* collectionTraverseBackward(Node&, unsigned count) const; + Node* collectionEnd() const { return nullptr; } + void collectionTraverseForward(Node*&, unsigned count, unsigned& traversedCount) const; + void collectionTraverseBackward(Node*&, unsigned count) const; bool collectionCanTraverseBackward() const { return true; } + void willValidateIndexCache() const { } private: explicit ChildNodeList(ContainerNode& parent); - virtual unsigned length() const override; - virtual Node* item(unsigned index) const override; - virtual Node* namedItem(const AtomicString&) const override; + unsigned length() const override; + Node* item(unsigned index) const override; + size_t memoryCost() const override { return m_indexCache.memoryCost(); } - virtual bool isChildNodeList() const override { return true; } + bool isChildNodeList() const override { return true; } Ref<ContainerNode> m_parent; - mutable CollectionIndexCache<ChildNodeList, Node> m_indexCache; + mutable CollectionIndexCache<ChildNodeList, Node*> m_indexCache; }; } // namespace WebCore - -#endif // ChildNodeList_h diff --git a/Source/WebCore/dom/ClassNodeList.cpp b/Source/WebCore/dom/ClassCollection.cpp index 2cd99b1eb..88af086a3 100644 --- a/Source/WebCore/dom/ClassNodeList.cpp +++ b/Source/WebCore/dom/ClassCollection.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007-2008, 2014, 2015 Apple Inc. All rights reserved. * Copyright (C) 2007 David Smith (catfish.man@gmail.com) * * Redistribution and use in source and binary forms, with or without @@ -11,7 +11,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -28,28 +28,22 @@ */ #include "config.h" -#include "ClassNodeList.h" +#include "ClassCollection.h" #include "NodeRareData.h" #include "StyledElement.h" namespace WebCore { -ClassNodeList::ClassNodeList(ContainerNode& rootNode, const String& classNames) - : LiveNodeList(rootNode, ClassNodeListType, InvalidateOnClassAttrChange) - , m_classNames(classNames, document().inQuirksMode()) - , m_originalClassNames(classNames) +Ref<ClassCollection> ClassCollection::create(ContainerNode& rootNode, CollectionType type, const AtomicString& classNames) { + ASSERT(type == ByClass); + return adoptRef(*new ClassCollection(rootNode, type, classNames)); } -ClassNodeList::~ClassNodeList() +ClassCollection::~ClassCollection() { - ownerNode().nodeLists()->removeCacheWithName(this, m_originalClassNames); -} - -bool ClassNodeList::nodeMatches(Element* testNode) const -{ - return nodeMatchesInlined(testNode); + ownerNode().nodeLists()->removeCachedCollection(this, m_originalClassNames); } } // namespace WebCore diff --git a/Source/WebCore/dom/ClassNodeList.h b/Source/WebCore/dom/ClassCollection.h index 79813e5cf..1af53cca8 100644 --- a/Source/WebCore/dom/ClassNodeList.h +++ b/Source/WebCore/dom/ClassCollection.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2014, 2015 Apple Inc. All rights reserved. * Copyright (C) 2007 David Smith (catfish.man@gmail.com) * * Redistribution and use in source and binary forms, with or without @@ -11,7 +11,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -27,49 +27,45 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ClassNodeList_h -#define ClassNodeList_h +#pragma once +#include "CachedHTMLCollection.h" #include "Element.h" -#include "LiveNodeList.h" -#include "Node.h" #include "SpaceSplitString.h" namespace WebCore { -class ClassNodeList : public LiveNodeList { +class ClassCollection final : public CachedHTMLCollection<ClassCollection, CollectionTypeTraits<ByClass>::traversalType> { public: - static PassRefPtr<ClassNodeList> create(ContainerNode& rootNode, const String& classNames) - { - return adoptRef(new ClassNodeList(rootNode, classNames)); - } + static Ref<ClassCollection> create(ContainerNode&, CollectionType, const AtomicString& classNames); - virtual ~ClassNodeList(); + virtual ~ClassCollection(); - bool nodeMatchesInlined(Element*) const; + bool elementMatches(Element&) const; private: - ClassNodeList(ContainerNode& rootNode, const String& classNames); - - virtual bool nodeMatches(Element*) const override; + ClassCollection(ContainerNode& rootNode, CollectionType, const AtomicString& classNames); SpaceSplitString m_classNames; - String m_originalClassNames; + AtomicString m_originalClassNames; }; -inline bool ClassNodeList::nodeMatchesInlined(Element* testNode) const +inline ClassCollection::ClassCollection(ContainerNode& rootNode, CollectionType type, const AtomicString& classNames) + : CachedHTMLCollection<ClassCollection, CollectionTypeTraits<ByClass>::traversalType>(rootNode, type) + , m_classNames(classNames, rootNode.document().inQuirksMode()) + , m_originalClassNames(classNames) { - if (!testNode->hasClass()) - return false; - if (!m_classNames.size()) +} + +inline bool ClassCollection::elementMatches(Element& element) const +{ + if (!element.hasClass()) return false; - // FIXME: DOM4 allows getElementsByClassName to return non StyledElement. - // https://bugs.webkit.org/show_bug.cgi?id=94718 - if (!testNode->isStyledElement()) + if (m_classNames.isEmpty()) return false; - return testNode->classNames().containsAll(m_classNames); + return element.classNames().containsAll(m_classNames); } } // namespace WebCore -#endif // ClassNodeList_h +SPECIALIZE_TYPE_TRAITS_HTMLCOLLECTION(ClassCollection, ByClass) diff --git a/Source/WebCore/dom/ClientRect.cpp b/Source/WebCore/dom/ClientRect.cpp index b1322228d..4a64e7551 100644 --- a/Source/WebCore/dom/ClientRect.cpp +++ b/Source/WebCore/dom/ClientRect.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR diff --git a/Source/WebCore/dom/ClientRect.h b/Source/WebCore/dom/ClientRect.h index dc2f0d2d5..2e9efb5b7 100644 --- a/Source/WebCore/dom/ClientRect.h +++ b/Source/WebCore/dom/ClientRect.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,12 +24,11 @@ * */ -#ifndef ClientRect_h -#define ClientRect_h +#pragma once #include "FloatRect.h" #include "ScriptWrappable.h" -#include <wtf/PassRefPtr.h> +#include <wtf/Ref.h> #include <wtf/RefCounted.h> namespace WebCore { @@ -38,9 +37,9 @@ namespace WebCore { class ClientRect : public ScriptWrappable, public RefCounted<ClientRect> { public: - static PassRefPtr<ClientRect> create() { return adoptRef(new ClientRect); } - static PassRefPtr<ClientRect> create(const IntRect& rect) { return adoptRef(new ClientRect(rect)); } - static PassRefPtr<ClientRect> create(const FloatRect& rect) { return adoptRef(new ClientRect(rect)); } + static Ref<ClientRect> create() { return adoptRef(*new ClientRect); } + static Ref<ClientRect> create(const IntRect& rect) { return adoptRef(*new ClientRect(rect)); } + static Ref<ClientRect> create(const FloatRect& rect) { return adoptRef(*new ClientRect(rect)); } float top() const { return m_rect.y(); } float right() const { return m_rect.maxX(); } @@ -50,13 +49,11 @@ namespace WebCore { float height() const { return m_rect.height(); } private: - ClientRect(); - explicit ClientRect(const IntRect&); - explicit ClientRect(const FloatRect&); + WEBCORE_EXPORT ClientRect(); + WEBCORE_EXPORT explicit ClientRect(const IntRect&); + WEBCORE_EXPORT explicit ClientRect(const FloatRect&); FloatRect m_rect; }; } // namespace WebCore - -#endif // ClientRect_h diff --git a/Source/WebCore/dom/ClientRect.idl b/Source/WebCore/dom/ClientRect.idl index 3dc5b03cf..cd9940a9d 100644 --- a/Source/WebCore/dom/ClientRect.idl +++ b/Source/WebCore/dom/ClientRect.idl @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -25,13 +25,15 @@ */ [ - ImplementationLacksVTable + ImplementationLacksVTable, + ExportMacro=WEBCORE_EXPORT, ] interface ClientRect { - readonly attribute float top; - readonly attribute float right; - readonly attribute float bottom; - readonly attribute float left; - readonly attribute float width; - readonly attribute float height; -}; + readonly attribute unrestricted float top; + readonly attribute unrestricted float right; + readonly attribute unrestricted float bottom; + readonly attribute unrestricted float left; + readonly attribute unrestricted float width; + readonly attribute unrestricted float height; + serializer = { attribute }; +}; diff --git a/Source/WebCore/dom/ClientRectList.cpp b/Source/WebCore/dom/ClientRectList.cpp index 95ec75802..e20fec43d 100644 --- a/Source/WebCore/dom/ClientRectList.cpp +++ b/Source/WebCore/dom/ClientRectList.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -27,7 +27,6 @@ #include "config.h" #include "ClientRectList.h" -#include "ExceptionCode.h" #include "ClientRect.h" namespace WebCore { @@ -39,8 +38,8 @@ ClientRectList::ClientRectList() ClientRectList::ClientRectList(const Vector<FloatQuad>& quads) { m_list.reserveInitialCapacity(quads.size()); - for (size_t i = 0; i < quads.size(); ++i) - m_list.append(ClientRect::create(quads[i].enclosingBoundingBox())); + for (auto& quad : quads) + m_list.uncheckedAppend(ClientRect::create(quad.enclosingBoundingBox())); } ClientRectList::~ClientRectList() @@ -55,12 +54,10 @@ unsigned ClientRectList::length() const ClientRect* ClientRectList::item(unsigned index) { if (index >= m_list.size()) { - // FIXME: this should throw an exception. - // ec = INDEX_SIZE_ERR; - return 0; + // FIXME: Should this throw an INDEX_SIZE_ERR exception? + return nullptr; } - - return m_list[index].get(); + return m_list[index].ptr(); } } // namespace WebCore diff --git a/Source/WebCore/dom/ClientRectList.h b/Source/WebCore/dom/ClientRectList.h index c421d8035..888430407 100644 --- a/Source/WebCore/dom/ClientRectList.h +++ b/Source/WebCore/dom/ClientRectList.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,11 +24,10 @@ * */ -#ifndef ClientRectList_h -#define ClientRectList_h +#pragma once #include "FloatQuad.h" -#include <wtf/PassRefPtr.h> +#include <wtf/Ref.h> #include <wtf/RefCounted.h> #include <wtf/Vector.h> @@ -38,20 +37,18 @@ namespace WebCore { class ClientRectList : public RefCounted<ClientRectList> { public: - static PassRefPtr<ClientRectList> create() { return adoptRef(new ClientRectList); } - static PassRefPtr<ClientRectList> create(const Vector<FloatQuad>& quads) { return adoptRef(new ClientRectList(quads)); } - ~ClientRectList(); + static Ref<ClientRectList> create() { return adoptRef(*new ClientRectList); } + static Ref<ClientRectList> create(const Vector<FloatQuad>& quads) { return adoptRef(*new ClientRectList(quads)); } + WEBCORE_EXPORT ~ClientRectList(); unsigned length() const; ClientRect* item(unsigned index); private: - ClientRectList(); - explicit ClientRectList(const Vector<FloatQuad>&); + WEBCORE_EXPORT ClientRectList(); + WEBCORE_EXPORT explicit ClientRectList(const Vector<FloatQuad>&); - Vector<RefPtr<ClientRect>> m_list; + Vector<Ref<ClientRect>> m_list; }; } // namespace WebCore - -#endif // ClientRectList_h diff --git a/Source/WebCore/dom/ClientRectList.idl b/Source/WebCore/dom/ClientRectList.idl index 1ad3b53c1..a5dbcf671 100644 --- a/Source/WebCore/dom/ClientRectList.idl +++ b/Source/WebCore/dom/ClientRectList.idl @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,9 +26,10 @@ [ ImplementationLacksVTable, + ExportMacro=WEBCORE_EXPORT, ] interface ClientRectList { readonly attribute unsigned long length; - getter ClientRect item([IsIndex, Default=Undefined] optional unsigned long index); + getter ClientRect item(unsigned long index); // FIXME: Fix list behavior to allow custom exceptions to be thrown. }; diff --git a/Source/WebCore/dom/Clipboard.h b/Source/WebCore/dom/Clipboard.h deleted file mode 100644 index 5ca2700b6..000000000 --- a/Source/WebCore/dom/Clipboard.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2001 Peter Kelly (pmk@post.com) - * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) - * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2013 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef Clipboard_h -#define Clipboard_h - -#include "CachedResourceHandle.h" -#include "ClipboardAccessPolicy.h" -#include "DragActions.h" -#include "DragImage.h" -#include "IntPoint.h" -#include <wtf/RefCounted.h> -#include <wtf/text/WTFString.h> - -namespace WebCore { - - class CachedImage; - class DataTransferItemList; - class DragData; - class DragImageLoader; - class Element; - class FileList; - class Pasteboard; - - class Clipboard : public RefCounted<Clipboard> { - public: - static PassRefPtr<Clipboard> createForCopyAndPaste(ClipboardAccessPolicy); - - ~Clipboard(); - - String dropEffect() const; - void setDropEffect(const String&); - - String effectAllowed() const; - void setEffectAllowed(const String&); - - Vector<String> types() const; - - PassRefPtr<FileList> files() const; - - void clearData(const String& type); - void clearData(); - - String getData(const String& type) const; - - bool setData(const String& type, const String& data); - - void setDragImage(Element*, int x, int y); - -#if ENABLE(DATA_TRANSFER_ITEMS) - PassRefPtr<DataTransferItemList> items() = 0; -#endif - - void setAccessPolicy(ClipboardAccessPolicy); - bool canReadTypes() const; - bool canReadData() const; - bool canWriteData() const; - - Pasteboard& pasteboard() { return *m_pasteboard; } - -#if ENABLE(DRAG_SUPPORT) - static PassRefPtr<Clipboard> createForDragAndDrop(); - static PassRefPtr<Clipboard> createForDragAndDrop(ClipboardAccessPolicy, const DragData&); - - bool dropEffectIsUninitialized() const { return m_dropEffect == "uninitialized"; } - - DragOperation sourceOperation() const; - DragOperation destinationOperation() const; - void setSourceOperation(DragOperation); - void setDestinationOperation(DragOperation); - - void setDragHasStarted() { m_shouldUpdateDragImage = true; } - DragImageRef createDragImage(IntPoint& dragLocation) const; - void updateDragImage(); -#endif - - private: - enum ClipboardType { CopyAndPaste, DragAndDrop }; - Clipboard(ClipboardAccessPolicy, PassOwnPtr<Pasteboard>, ClipboardType = CopyAndPaste, bool forFileDrag = false); - -#if ENABLE(DRAG_SUPPORT) - bool canSetDragImage() const; -#endif - - ClipboardAccessPolicy m_policy; - OwnPtr<Pasteboard> m_pasteboard; - -#if ENABLE(DRAG_SUPPORT) - bool m_forDrag; - bool m_forFileDrag; - String m_dropEffect; - String m_effectAllowed; - bool m_shouldUpdateDragImage; - IntPoint m_dragLocation; - CachedResourceHandle<CachedImage> m_dragImage; - RefPtr<Element> m_dragImageElement; - OwnPtr<DragImageLoader> m_dragImageLoader; -#endif - }; - -} // namespace WebCore - -#endif // Clipboard_h diff --git a/Source/WebCore/dom/ClipboardEvent.cpp b/Source/WebCore/dom/ClipboardEvent.cpp index 421b8876a..bdc4b9a53 100644 --- a/Source/WebCore/dom/ClipboardEvent.cpp +++ b/Source/WebCore/dom/ClipboardEvent.cpp @@ -23,17 +23,13 @@ #include "config.h" #include "ClipboardEvent.h" -#include "Clipboard.h" -#include "EventNames.h" +#include "DataTransfer.h" namespace WebCore { -ClipboardEvent::ClipboardEvent() -{ -} - -ClipboardEvent::ClipboardEvent(const AtomicString& eventType, bool canBubble, bool cancelable, PassRefPtr<Clipboard> clipboard) - : Event(eventType, canBubble, cancelable), m_clipboard(clipboard) +ClipboardEvent::ClipboardEvent(const AtomicString& type, const Init& init, IsTrusted isTrusted) + : Event(type, init, isTrusted) + , m_clipboardData(init.clipboardData) { } @@ -43,8 +39,7 @@ ClipboardEvent::~ClipboardEvent() EventInterface ClipboardEvent::eventInterface() const { - // Notice that there is no ClipboardEvent.idl. - return EventInterfaceType; + return ClipboardEventInterfaceType; } bool ClipboardEvent::isClipboardEvent() const diff --git a/Source/WebCore/dom/ClipboardEvent.h b/Source/WebCore/dom/ClipboardEvent.h index b8474337f..c4993558a 100644 --- a/Source/WebCore/dom/ClipboardEvent.h +++ b/Source/WebCore/dom/ClipboardEvent.h @@ -21,40 +21,37 @@ * */ -#ifndef ClipboardEvent_h -#define ClipboardEvent_h +#pragma once #include "Event.h" namespace WebCore { - class Clipboard; +class DataTransfer; - class ClipboardEvent : public Event { - public: - virtual ~ClipboardEvent(); +class ClipboardEvent final : public Event { +public: + virtual ~ClipboardEvent(); - static PassRefPtr<ClipboardEvent> create() - { - return adoptRef(new ClipboardEvent); - } - static PassRefPtr<ClipboardEvent> create(const AtomicString& type, bool canBubbleArg, bool cancelableArg, PassRefPtr<Clipboard> clipboardArg) - { - return adoptRef(new ClipboardEvent(type, canBubbleArg, cancelableArg, clipboardArg)); - } + struct Init : EventInit { + RefPtr<DataTransfer> clipboardData; + }; - Clipboard* clipboard() const { return m_clipboard.get(); } + static Ref<ClipboardEvent> create(const AtomicString& type, const Init& init, IsTrusted isTrusted = IsTrusted::No) + { + auto event = adoptRef(*new ClipboardEvent(type, init, isTrusted)); + return event; + } - private: - ClipboardEvent(); - ClipboardEvent(const AtomicString& type, bool canBubbleArg, bool cancelableArg, PassRefPtr<Clipboard>); + DataTransfer* clipboardData() const { return m_clipboardData.get(); } - virtual EventInterface eventInterface() const override; - virtual bool isClipboardEvent() const override; +private: + ClipboardEvent(const AtomicString& type, const Init&, IsTrusted); - RefPtr<Clipboard> m_clipboard; - }; + EventInterface eventInterface() const final; + bool isClipboardEvent() const final; -} // namespace WebCore + RefPtr<DataTransfer> m_clipboardData; +}; -#endif // ClipboardEvent_h +} // namespace WebCore diff --git a/Source/WebCore/dom/ClipboardEvent.idl b/Source/WebCore/dom/ClipboardEvent.idl new file mode 100644 index 000000000..4a41992ac --- /dev/null +++ b/Source/WebCore/dom/ClipboardEvent.idl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +[ + Constructor(DOMString type, optional ClipboardEventInit eventInitDict) +] interface ClipboardEvent : Event { + readonly attribute DataTransfer? clipboardData; +}; + +dictionary ClipboardEventInit : EventInit { + DataTransfer? clipboardData = null; +}; diff --git a/Source/WebCore/dom/CollectionIndexCache.cpp b/Source/WebCore/dom/CollectionIndexCache.cpp new file mode 100644 index 000000000..65bea699e --- /dev/null +++ b/Source/WebCore/dom/CollectionIndexCache.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "CollectionIndexCache.h" + +#include "CommonVM.h" +#include "DOMWindow.h" +#include "JSDOMBinding.h" + +namespace WebCore { + +void reportExtraMemoryAllocatedForCollectionIndexCache(size_t cost) +{ + JSC::VM& vm = commonVM(); + JSC::JSLockHolder lock(vm); + // FIXME: Adopt reportExtraMemoryVisited, and switch to reportExtraMemoryAllocated. + // https://bugs.webkit.org/show_bug.cgi?id=142595 + vm.heap.deprecatedReportExtraMemory(cost); +} + +} diff --git a/Source/WebCore/dom/CollectionIndexCache.h b/Source/WebCore/dom/CollectionIndexCache.h index 8a761f9fb..048c9ae63 100644 --- a/Source/WebCore/dom/CollectionIndexCache.h +++ b/Source/WebCore/dom/CollectionIndexCache.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Apple Inc. All rights reserved. + * Copyright (C) 2013-2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,150 +23,202 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef CollectionIndexCache_h -#define CollectionIndexCache_h +#pragma once + +#include <wtf/Vector.h> namespace WebCore { -template <class Collection, class NodeType> +WEBCORE_EXPORT void reportExtraMemoryAllocatedForCollectionIndexCache(size_t); + +template <class Collection, class Iterator> class CollectionIndexCache { public: - CollectionIndexCache(); + explicit CollectionIndexCache(const Collection&); + + typedef typename std::iterator_traits<Iterator>::value_type NodeType; unsigned nodeCount(const Collection&); NodeType* nodeAt(const Collection&, unsigned index); - void invalidate(); + bool hasValidCache(const Collection& collection) const { return m_current != collection.collectionEnd() || m_nodeCountValid || m_listValid; } + void invalidate(const Collection&); + size_t memoryCost() { return m_cachedList.capacity() * sizeof(NodeType*); } private: - NodeType* nodeBeforeCached(const Collection&, unsigned); - NodeType* nodeAfterCached(const Collection&, unsigned); + unsigned computeNodeCountUpdatingListCache(const Collection&); + NodeType* traverseBackwardTo(const Collection&, unsigned); + NodeType* traverseForwardTo(const Collection&, unsigned); - NodeType* m_currentNode; + Iterator m_current; unsigned m_currentIndex; unsigned m_nodeCount; - bool m_nodeCountValid; + Vector<NodeType*> m_cachedList; + bool m_nodeCountValid : 1; + bool m_listValid : 1; }; -template <class Collection, class NodeType> -inline CollectionIndexCache<Collection, NodeType>::CollectionIndexCache() - : m_currentNode(nullptr) +template <class Collection, class Iterator> +inline CollectionIndexCache<Collection, Iterator>::CollectionIndexCache(const Collection& collection) + : m_current(collection.collectionEnd()) , m_currentIndex(0) , m_nodeCount(0) , m_nodeCountValid(false) + , m_listValid(false) { } -template <class Collection, class NodeType> -inline unsigned CollectionIndexCache<Collection, NodeType>::nodeCount(const Collection& collection) +template <class Collection, class Iterator> +inline unsigned CollectionIndexCache<Collection, Iterator>::nodeCount(const Collection& collection) { if (!m_nodeCountValid) { - if (auto first = collection.collectionFirst()) { - unsigned count; - collection.collectionTraverseForward(*first, std::numeric_limits<unsigned>::max(), count); - m_nodeCount = count + 1; - } else - m_nodeCount = 0; + if (!hasValidCache(collection)) + collection.willValidateIndexCache(); + m_nodeCount = computeNodeCountUpdatingListCache(collection); m_nodeCountValid = true; } return m_nodeCount; } -template <class Collection, class NodeType> -inline NodeType* CollectionIndexCache<Collection, NodeType>::nodeBeforeCached(const Collection& collection, unsigned index) +template <class Collection, class Iterator> +unsigned CollectionIndexCache<Collection, Iterator>::computeNodeCountUpdatingListCache(const Collection& collection) +{ + auto current = collection.collectionBegin(); + auto end = collection.collectionEnd(); + if (current == end) + return 0; + + unsigned oldCapacity = m_cachedList.capacity(); + while (current != end) { + m_cachedList.append(&*current); + unsigned traversed; + collection.collectionTraverseForward(current, 1, traversed); + ASSERT(traversed == (current != end ? 1 : 0)); + } + m_listValid = true; + + if (unsigned capacityDifference = m_cachedList.capacity() - oldCapacity) + reportExtraMemoryAllocatedForCollectionIndexCache(capacityDifference * sizeof(NodeType*)); + + return m_cachedList.size(); +} + +template <class Collection, class Iterator> +inline typename CollectionIndexCache<Collection, Iterator>::NodeType* CollectionIndexCache<Collection, Iterator>::traverseBackwardTo(const Collection& collection, unsigned index) { - ASSERT(m_currentNode); + ASSERT(m_current != collection.collectionEnd()); ASSERT(index < m_currentIndex); bool firstIsCloser = index < m_currentIndex - index; if (firstIsCloser || !collection.collectionCanTraverseBackward()) { - m_currentNode = collection.collectionFirst(); + m_current = collection.collectionBegin(); m_currentIndex = 0; if (index) - m_currentNode = collection.collectionTraverseForward(*m_currentNode, index, m_currentIndex); - ASSERT(m_currentNode); - return m_currentNode; + collection.collectionTraverseForward(m_current, index, m_currentIndex); + ASSERT(m_current != collection.collectionEnd()); + return &*m_current; } - m_currentNode = collection.collectionTraverseBackward(*m_currentNode, m_currentIndex - index); + collection.collectionTraverseBackward(m_current, m_currentIndex - index); m_currentIndex = index; - ASSERT(m_currentNode); - return m_currentNode; + ASSERT(m_current != collection.collectionEnd()); + return &*m_current; } -template <class Collection, class NodeType> -inline NodeType* CollectionIndexCache<Collection, NodeType>::nodeAfterCached(const Collection& collection, unsigned index) +template <class Collection, class Iterator> +inline typename CollectionIndexCache<Collection, Iterator>::NodeType* CollectionIndexCache<Collection, Iterator>::traverseForwardTo(const Collection& collection, unsigned index) { - ASSERT(m_currentNode); + ASSERT(m_current != collection.collectionEnd()); ASSERT(index > m_currentIndex); ASSERT(!m_nodeCountValid || index < m_nodeCount); bool lastIsCloser = m_nodeCountValid && m_nodeCount - index < index - m_currentIndex; if (lastIsCloser && collection.collectionCanTraverseBackward()) { - m_currentNode = collection.collectionLast(); + ASSERT(hasValidCache(collection)); + m_current = collection.collectionLast(); if (index < m_nodeCount - 1) - m_currentNode = collection.collectionTraverseBackward(*m_currentNode, m_nodeCount - index - 1); + collection.collectionTraverseBackward(m_current, m_nodeCount - index - 1); m_currentIndex = index; - ASSERT(m_currentNode); - return m_currentNode; + ASSERT(m_current != collection.collectionEnd()); + return &*m_current; } + if (!hasValidCache(collection)) + collection.willValidateIndexCache(); + unsigned traversedCount; - m_currentNode = collection.collectionTraverseForward(*m_currentNode, index - m_currentIndex, traversedCount); + collection.collectionTraverseForward(m_current, index - m_currentIndex, traversedCount); m_currentIndex = m_currentIndex + traversedCount; - ASSERT(m_currentNode || m_currentIndex < index); - - if (!m_currentNode && !m_nodeCountValid) { + if (m_current == collection.collectionEnd()) { + ASSERT(m_currentIndex < index); // Failed to find the index but at least we now know the size. m_nodeCount = m_currentIndex + 1; m_nodeCountValid = true; + return nullptr; } - return m_currentNode; + ASSERT(hasValidCache(collection)); + return &*m_current; } -template <class Collection, class NodeType> -inline NodeType* CollectionIndexCache<Collection, NodeType>::nodeAt(const Collection& collection, unsigned index) +template <class Collection, class Iterator> +inline typename CollectionIndexCache<Collection, Iterator>::NodeType* CollectionIndexCache<Collection, Iterator>::nodeAt(const Collection& collection, unsigned index) { if (m_nodeCountValid && index >= m_nodeCount) return nullptr; - if (m_currentNode) { + if (m_listValid) + return m_cachedList[index]; + + auto end = collection.collectionEnd(); + if (m_current != end) { if (index > m_currentIndex) - return nodeAfterCached(collection, index); + return traverseForwardTo(collection, index); if (index < m_currentIndex) - return nodeBeforeCached(collection, index); - return m_currentNode; + return traverseBackwardTo(collection, index); + return &*m_current; } bool lastIsCloser = m_nodeCountValid && m_nodeCount - index < index; if (lastIsCloser && collection.collectionCanTraverseBackward()) { - m_currentNode = collection.collectionLast(); + ASSERT(hasValidCache(collection)); + m_current = collection.collectionLast(); if (index < m_nodeCount - 1) - m_currentNode = collection.collectionTraverseBackward(*m_currentNode, m_nodeCount - index - 1); + collection.collectionTraverseBackward(m_current, m_nodeCount - index - 1); m_currentIndex = index; - ASSERT(m_currentNode); - return m_currentNode; + ASSERT(m_current != end); + return &*m_current; } - m_currentNode = collection.collectionFirst(); + if (!hasValidCache(collection)) + collection.willValidateIndexCache(); + + m_current = collection.collectionBegin(); m_currentIndex = 0; - if (index && m_currentNode) { - m_currentNode = collection.collectionTraverseForward(*m_currentNode, index, m_currentIndex); - ASSERT(m_currentNode || m_currentIndex < index); + if (index && m_current != end) { + collection.collectionTraverseForward(m_current, index, m_currentIndex); + ASSERT(m_current != end || m_currentIndex < index); + } + if (m_current == end) { + // Failed to find the index but at least we now know the size. + m_nodeCount = index ? m_currentIndex + 1 : 0; + m_nodeCountValid = true; + return nullptr; } - return m_currentNode; + ASSERT(hasValidCache(collection)); + return &*m_current; } -template <class Collection, class NodeType> -void CollectionIndexCache<Collection, NodeType>::invalidate() +template <class Collection, class Iterator> +void CollectionIndexCache<Collection, Iterator>::invalidate(const Collection& collection) { - m_currentNode = nullptr; + m_current = collection.collectionEnd(); m_nodeCountValid = false; + m_listValid = false; + m_cachedList.shrink(0); } -} -#endif +} diff --git a/Source/WebCore/dom/Comment.cpp b/Source/WebCore/dom/Comment.cpp index 51300e9e8..731436894 100644 --- a/Source/WebCore/dom/Comment.cpp +++ b/Source/WebCore/dom/Comment.cpp @@ -31,19 +31,14 @@ inline Comment::Comment(Document& document, const String& text) { } -PassRefPtr<Comment> Comment::create(Document& document, const String& text) +Ref<Comment> Comment::create(Document& document, const String& text) { - return adoptRef(new Comment(document, text)); -} - -PassRefPtr<Comment> Comment::create(ScriptExecutionContext& context, const String& text) -{ - return adoptRef(new Comment(toDocument(context), text)); + return adoptRef(*new Comment(document, text)); } String Comment::nodeName() const { - return commentAtom.string(); + return ASCIILiteral("#comment"); } Node::NodeType Comment::nodeType() const @@ -51,9 +46,9 @@ Node::NodeType Comment::nodeType() const return COMMENT_NODE; } -PassRefPtr<Node> Comment::cloneNode(bool /*deep*/) +Ref<Node> Comment::cloneNodeInternal(Document& targetDocument, CloningOperation) { - return create(document(), data()); + return create(targetDocument, data()); } bool Comment::childTypeAllowed(NodeType) const diff --git a/Source/WebCore/dom/Comment.h b/Source/WebCore/dom/Comment.h index 48729b6ac..fe7d34834 100644 --- a/Source/WebCore/dom/Comment.h +++ b/Source/WebCore/dom/Comment.h @@ -20,8 +20,7 @@ * */ -#ifndef Comment_h -#define Comment_h +#pragma once #include "CharacterData.h" @@ -29,24 +28,19 @@ namespace WebCore { class Comment final : public CharacterData { public: - static PassRefPtr<Comment> create(Document&, const String&); - static PassRefPtr<Comment> create(ScriptExecutionContext&, const String&); + static Ref<Comment> create(Document&, const String&); private: Comment(Document&, const String&); - virtual String nodeName() const override; - virtual NodeType nodeType() const override; - virtual PassRefPtr<Node> cloneNode(bool deep) override; - virtual bool childTypeAllowed(NodeType) const override; + String nodeName() const override; + NodeType nodeType() const override; + Ref<Node> cloneNodeInternal(Document&, CloningOperation) override; + bool childTypeAllowed(NodeType) const override; }; -inline bool isComment(const Node& node) { return node.nodeType() == Node::COMMENT_NODE; } -void isComment(const Comment&); // Catch unnecessary runtime check of type known at compile time. -void isComment(const ContainerNode&); // Catch unnecessary runtime check of type known at compile time. - -NODE_TYPE_CASTS(Comment) - } // namespace WebCore -#endif // Comment_h +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::Comment) + static bool isType(const WebCore::Node& node) { return node.nodeType() == WebCore::Node::COMMENT_NODE; } +SPECIALIZE_TYPE_TRAITS_END() diff --git a/Source/WebCore/dom/Comment.idl b/Source/WebCore/dom/Comment.idl index d928c6ef1..579adb4af 100644 --- a/Source/WebCore/dom/Comment.idl +++ b/Source/WebCore/dom/Comment.idl @@ -18,8 +18,9 @@ */ [ - Constructor([Default=NullString] optional DOMString data), - ConstructorCallWith=ScriptExecutionContext + Constructor(optional DOMString data), + ConstructorCallWith=Document, + JSGenerateToJSObject ] interface Comment : CharacterData { }; diff --git a/Source/WebCore/dom/ComposedTreeAncestorIterator.h b/Source/WebCore/dom/ComposedTreeAncestorIterator.h new file mode 100644 index 000000000..c661bce5c --- /dev/null +++ b/Source/WebCore/dom/ComposedTreeAncestorIterator.h @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "HTMLSlotElement.h" +#include "PseudoElement.h" +#include "ShadowRoot.h" + +namespace WebCore { + +class HTMLSlotElement; + +class ComposedTreeAncestorIterator { +public: + ComposedTreeAncestorIterator(); + ComposedTreeAncestorIterator(Node& current); + + Element& operator*() { return get(); } + Element* operator->() { return &get(); } + + bool operator==(const ComposedTreeAncestorIterator& other) const { return m_current == other.m_current; } + bool operator!=(const ComposedTreeAncestorIterator& other) const { return m_current != other.m_current; } + + ComposedTreeAncestorIterator& operator++() { return traverseParent(); } + + Element& get() { return downcast<Element>(*m_current); } + ComposedTreeAncestorIterator& traverseParent(); + +private: + void traverseParentInShadowTree(); + + Node* m_current { 0 }; +}; + +inline ComposedTreeAncestorIterator::ComposedTreeAncestorIterator() +{ +} + +inline ComposedTreeAncestorIterator::ComposedTreeAncestorIterator(Node& current) + : m_current(¤t) +{ + ASSERT(!is<ShadowRoot>(m_current)); +} + +inline ComposedTreeAncestorIterator& ComposedTreeAncestorIterator::traverseParent() +{ + auto* parent = m_current->parentNode(); + if (!parent) { + m_current = nullptr; + return *this; + } + if (is<ShadowRoot>(*parent)) { + m_current = downcast<ShadowRoot>(*parent).host(); + return *this; + } + if (!is<Element>(*parent)) { + m_current = nullptr; + return *this; + }; + + if (auto* shadowRoot = parent->shadowRoot()) { + m_current = shadowRoot->findAssignedSlot(*m_current); + return *this; + } + + m_current = parent; + return *this; +} + +class ComposedTreeAncestorAdapter { +public: + using iterator = ComposedTreeAncestorIterator; + + ComposedTreeAncestorAdapter(Node& node) + : m_node(node) + { } + + iterator begin() + { + if (is<ShadowRoot>(m_node)) + return iterator(*downcast<ShadowRoot>(m_node).host()); + if (is<PseudoElement>(m_node)) + return iterator(*downcast<PseudoElement>(m_node).hostElement()); + return iterator(m_node).traverseParent(); + } + iterator end() + { + return iterator(); + } + Element* first() + { + auto it = begin(); + if (it == end()) + return nullptr; + return &it.get(); + } + +private: + Node& m_node; +}; + +// FIXME: We should have const versions too. +inline ComposedTreeAncestorAdapter composedTreeAncestors(Node& node) +{ + return ComposedTreeAncestorAdapter(node); +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/ComposedTreeIterator.cpp b/Source/WebCore/dom/ComposedTreeIterator.cpp new file mode 100644 index 000000000..af554ac04 --- /dev/null +++ b/Source/WebCore/dom/ComposedTreeIterator.cpp @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2015-2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ComposedTreeIterator.h" + +#include "HTMLSlotElement.h" +#include "TextStream.h" + +namespace WebCore { + +ComposedTreeIterator::Context::Context() +{ +} + +ComposedTreeIterator::Context::Context(ContainerNode& root, FirstChildTag) + : iterator(root, ElementAndTextDescendantIterator::FirstChild) +{ +} + +ComposedTreeIterator::Context::Context(ContainerNode& root, Node& node) + : iterator(root, &node) +{ +} + +ComposedTreeIterator::Context::Context(ContainerNode& root, Node& node, SlottedTag) + : iterator(root, &node) + , end(iterator) +{ + end.traverseNextSibling(); +} + +ComposedTreeIterator::ComposedTreeIterator(ContainerNode& root, FirstChildTag) + : m_rootIsInShadowTree(root.isInShadowTree()) +{ + ASSERT(!is<ShadowRoot>(root)); + + if (is<HTMLSlotElement>(root)) { + auto& slot = downcast<HTMLSlotElement>(root); + if (auto* assignedNodes = slot.assignedNodes()) { + initializeContextStack(root, *assignedNodes->at(0)); + return; + } + } + if (auto* shadowRoot = root.shadowRoot()) { + ElementAndTextDescendantIterator firstChild(*shadowRoot, ElementAndTextDescendantIterator::FirstChild); + initializeContextStack(root, firstChild ? *firstChild : root); + return; + } + + m_contextStack.uncheckedAppend(Context(root, FirstChild)); +} + +ComposedTreeIterator::ComposedTreeIterator(ContainerNode& root, Node& current) + : m_rootIsInShadowTree(root.isInShadowTree()) +{ + ASSERT(!is<ShadowRoot>(root)); + ASSERT(!is<ShadowRoot>(current)); + + bool mayNeedShadowStack = root.shadowRoot() || (¤t != &root && current.parentNode() != &root); + if (mayNeedShadowStack) + initializeContextStack(root, current); + else + m_contextStack.uncheckedAppend(Context(root, current)); +} + +void ComposedTreeIterator::initializeContextStack(ContainerNode& root, Node& current) +{ + // This code sets up the iterator for arbitrary node/root pair. It is not needed in common cases + // or completes fast because node and root are close (like in composedTreeChildren(*parent).at(node) case). + auto* node = ¤t; + auto* contextCurrent = node; + size_t currentSlotNodeIndex = notFound; + while (node != &root) { + auto* parent = node->parentNode(); + if (!parent) { + *this = { }; + return; + } + if (is<ShadowRoot>(*parent)) { + auto& shadowRoot = downcast<ShadowRoot>(*parent); + m_contextStack.append(Context(shadowRoot, *contextCurrent)); + m_contextStack.last().slotNodeIndex = currentSlotNodeIndex; + + node = shadowRoot.host(); + contextCurrent = node; + currentSlotNodeIndex = notFound; + continue; + } + if (auto* shadowRoot = parent->shadowRoot()) { + m_contextStack.append(Context(*parent, *contextCurrent, Context::Slotted)); + m_contextStack.last().slotNodeIndex = currentSlotNodeIndex; + + auto* assignedSlot = shadowRoot->findAssignedSlot(*node); + if (assignedSlot) { + currentSlotNodeIndex = assignedSlot->assignedNodes()->find(node); + ASSERT(currentSlotNodeIndex != notFound); + node = assignedSlot; + contextCurrent = assignedSlot; + continue; + } + // The node is not part of the composed tree. + *this = { }; + return; + } + node = parent; + } + m_contextStack.append(Context(root, *contextCurrent)); + m_contextStack.last().slotNodeIndex = currentSlotNodeIndex; + + m_contextStack.reverse(); +} + +void ComposedTreeIterator::dropAssertions() +{ + for (auto& context : m_contextStack) + context.iterator.dropAssertions(); + m_didDropAssertions = true; +} + +void ComposedTreeIterator::traverseShadowRoot(ShadowRoot& shadowRoot) +{ + Context shadowContext(shadowRoot, FirstChild); + if (!shadowContext.iterator) { + // Empty shadow root. + traverseNextSkippingChildren(); + return; + } + + if (m_didDropAssertions) + shadowContext.iterator.dropAssertions(); + + m_contextStack.append(WTFMove(shadowContext)); +} + +void ComposedTreeIterator::traverseNextInShadowTree() +{ + ASSERT(m_contextStack.size() > 1 || m_rootIsInShadowTree); + + if (is<HTMLSlotElement>(current())) { + auto& slot = downcast<HTMLSlotElement>(current()); + if (auto* assignedNodes = slot.assignedNodes()) { + context().slotNodeIndex = 0; + auto* assignedNode = assignedNodes->at(0); + m_contextStack.append(Context(*assignedNode->parentElement(), *assignedNode, Context::Slotted)); + return; + } + } + + context().iterator.traverseNext(); + + if (context().iterator == context().end) + traverseNextLeavingContext(); +} + +void ComposedTreeIterator::traverseNextLeavingContext() +{ + while (context().iterator == context().end && m_contextStack.size() > 1) { + m_contextStack.removeLast(); + if (is<HTMLSlotElement>(current()) && advanceInSlot(1)) + return; + if (context().iterator == context().end) + return; + context().iterator.traverseNextSkippingChildren(); + } +} + +bool ComposedTreeIterator::advanceInSlot(int direction) +{ + ASSERT(context().slotNodeIndex != notFound); + + auto& assignedNodes = *downcast<HTMLSlotElement>(current()).assignedNodes(); + // It is fine to underflow this. + context().slotNodeIndex += direction; + if (context().slotNodeIndex >= assignedNodes.size()) + return false; + + auto* slotNode = assignedNodes.at(context().slotNodeIndex); + m_contextStack.append(Context(*slotNode->parentElement(), *slotNode, Context::Slotted)); + return true; +} + +void ComposedTreeIterator::traverseSiblingInSlot(int direction) +{ + ASSERT(m_contextStack.size() > 1); + ASSERT(current().parentNode()->shadowRoot()); + + m_contextStack.removeLast(); + + if (!advanceInSlot(direction)) + *this = { }; +} + +String composedTreeAsText(ContainerNode& root, ComposedTreeAsTextMode mode) +{ + TextStream stream; + auto descendants = composedTreeDescendants(root); + for (auto it = descendants.begin(), end = descendants.end(); it != end; ++it) { + writeIndent(stream, it.depth()); + + if (is<Text>(*it)) { + stream << "#text"; + if (mode == ComposedTreeAsTextMode::WithPointers) + stream << " " << &*it; + stream << "\n"; + continue; + } + auto& element = downcast<Element>(*it); + stream << element.localName(); + if (element.shadowRoot()) + stream << " (shadow root)"; + if (mode == ComposedTreeAsTextMode::WithPointers) + stream << " " << &*it; + stream << "\n"; + } + return stream.release(); +} + +} diff --git a/Source/WebCore/dom/ComposedTreeIterator.h b/Source/WebCore/dom/ComposedTreeIterator.h new file mode 100644 index 000000000..89740cfb8 --- /dev/null +++ b/Source/WebCore/dom/ComposedTreeIterator.h @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2015-2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "ElementAndTextDescendantIterator.h" +#include "ShadowRoot.h" + +namespace WebCore { + +class HTMLSlotElement; + +class ComposedTreeIterator { +public: + ComposedTreeIterator(); + enum FirstChildTag { FirstChild }; + ComposedTreeIterator(ContainerNode& root, FirstChildTag); + ComposedTreeIterator(ContainerNode& root, Node& current); + + Node& operator*() { return current(); } + Node* operator->() { return ¤t(); } + + bool operator==(const ComposedTreeIterator& other) const { return context().iterator == other.context().iterator; } + bool operator!=(const ComposedTreeIterator& other) const { return context().iterator != other.context().iterator; } + + ComposedTreeIterator& operator++() { return traverseNext(); } + + ComposedTreeIterator& traverseNext(); + ComposedTreeIterator& traverseNextSkippingChildren(); + ComposedTreeIterator& traverseNextSibling(); + ComposedTreeIterator& traversePreviousSibling(); + + unsigned depth() const; + + void dropAssertions(); + +private: + void initializeContextStack(ContainerNode& root, Node& current); + void traverseNextInShadowTree(); + void traverseNextLeavingContext(); + void traverseShadowRoot(ShadowRoot&); + bool advanceInSlot(int direction); + void traverseSiblingInSlot(int direction); + + struct Context { + Context(); + Context(ContainerNode& root, FirstChildTag); + Context(ContainerNode& root, Node& node); + + enum SlottedTag { Slotted }; + Context(ContainerNode& root, Node& node, SlottedTag); + ElementAndTextDescendantIterator iterator; + ElementAndTextDescendantIterator end; + size_t slotNodeIndex { notFound }; + }; + Context& context() { return m_contextStack.last(); } + const Context& context() const { return m_contextStack.last(); } + Node& current() { return *context().iterator; } + + bool m_rootIsInShadowTree { false }; + bool m_didDropAssertions { false }; + Vector<Context, 8> m_contextStack; +}; + +inline ComposedTreeIterator::ComposedTreeIterator() +{ + m_contextStack.uncheckedAppend({ }); +} + +inline ComposedTreeIterator& ComposedTreeIterator::traverseNext() +{ + if (auto* shadowRoot = context().iterator->shadowRoot()) { + traverseShadowRoot(*shadowRoot); + return *this; + } + + if (m_contextStack.size() > 1 || m_rootIsInShadowTree) { + traverseNextInShadowTree(); + return *this; + } + + context().iterator.traverseNext(); + return *this; +} + +inline ComposedTreeIterator& ComposedTreeIterator::traverseNextSkippingChildren() +{ + context().iterator.traverseNextSkippingChildren(); + + if (context().iterator == context().end) + traverseNextLeavingContext(); + + return *this; +} + +inline ComposedTreeIterator& ComposedTreeIterator::traverseNextSibling() +{ + if (current().parentNode()->shadowRoot()) { + traverseSiblingInSlot(1); + return *this; + } + context().iterator.traverseNextSibling(); + return *this; +} + +inline ComposedTreeIterator& ComposedTreeIterator::traversePreviousSibling() +{ + if (current().parentNode()->shadowRoot()) { + traverseSiblingInSlot(-1); + return *this; + } + context().iterator.traversePreviousSibling(); + return *this; +} + +inline unsigned ComposedTreeIterator::depth() const +{ + unsigned depth = 0; + for (auto& context : m_contextStack) + depth += context.iterator.depth(); + return depth; +} + +class ComposedTreeDescendantAdapter { +public: + ComposedTreeDescendantAdapter(ContainerNode& parent) + : m_parent(parent) + { } + + ComposedTreeIterator begin() { return ComposedTreeIterator(m_parent, ComposedTreeIterator::FirstChild); } + ComposedTreeIterator end() { return { }; } + ComposedTreeIterator at(const Node& child) { return ComposedTreeIterator(m_parent, const_cast<Node&>(child)); } + +private: + ContainerNode& m_parent; +}; + +class ComposedTreeChildAdapter { +public: + class Iterator : public ComposedTreeIterator { + public: + Iterator() = default; + explicit Iterator(ContainerNode& root) + : ComposedTreeIterator(root, ComposedTreeIterator::FirstChild) + { } + Iterator(ContainerNode& root, Node& current) + : ComposedTreeIterator(root, current) + { } + + Iterator& operator++() { return static_cast<Iterator&>(traverseNextSibling()); } + Iterator& operator--() { return static_cast<Iterator&>(traversePreviousSibling()); } + }; + + ComposedTreeChildAdapter(ContainerNode& parent) + : m_parent(parent) + { } + + Iterator begin() { return Iterator(m_parent); } + Iterator end() { return { }; } + Iterator at(const Node& child) { return Iterator(m_parent, const_cast<Node&>(child)); } + +private: + ContainerNode& m_parent; +}; + +// FIXME: We should have const versions too. +inline ComposedTreeDescendantAdapter composedTreeDescendants(ContainerNode& parent) +{ + return ComposedTreeDescendantAdapter(parent); +} + +inline ComposedTreeChildAdapter composedTreeChildren(ContainerNode& parent) +{ + return ComposedTreeChildAdapter(parent); +} + +enum class ComposedTreeAsTextMode { Normal, WithPointers }; +WEBCORE_EXPORT String composedTreeAsText(ContainerNode& root, ComposedTreeAsTextMode = ComposedTreeAsTextMode::Normal); + +} // namespace WebCore diff --git a/Source/WebCore/dom/CompositionEvent.cpp b/Source/WebCore/dom/CompositionEvent.cpp index df6a2b8f9..3e5ec89f7 100644 --- a/Source/WebCore/dom/CompositionEvent.cpp +++ b/Source/WebCore/dom/CompositionEvent.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -27,26 +27,20 @@ #include "config.h" #include "CompositionEvent.h" -#include "EventNames.h" - namespace WebCore { -CompositionEventInit::CompositionEventInit() -{ -} - CompositionEvent::CompositionEvent() { } -CompositionEvent::CompositionEvent(const AtomicString& type, PassRefPtr<AbstractView> view, const String& data) +CompositionEvent::CompositionEvent(const AtomicString& type, DOMWindow* view, const String& data) : UIEvent(type, true, true, view, 0) , m_data(data) { } -CompositionEvent::CompositionEvent(const AtomicString& type, const CompositionEventInit& initializer) - : UIEvent(type, initializer) +CompositionEvent::CompositionEvent(const AtomicString& type, const Init& initializer, IsTrusted isTrusted) + : UIEvent(type, initializer, isTrusted) , m_data(initializer.data) { } @@ -55,7 +49,7 @@ CompositionEvent::~CompositionEvent() { } -void CompositionEvent::initCompositionEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<AbstractView> view, const String& data) +void CompositionEvent::initCompositionEvent(const AtomicString& type, bool canBubble, bool cancelable, DOMWindow* view, const String& data) { if (dispatched()) return; @@ -70,4 +64,9 @@ EventInterface CompositionEvent::eventInterface() const return CompositionEventInterfaceType; } +bool CompositionEvent::isCompositionEvent() const +{ + return true; +} + } // namespace WebCore diff --git a/Source/WebCore/dom/CompositionEvent.h b/Source/WebCore/dom/CompositionEvent.h index 89f69703b..8ff02ec96 100644 --- a/Source/WebCore/dom/CompositionEvent.h +++ b/Source/WebCore/dom/CompositionEvent.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,52 +24,51 @@ * */ -#ifndef CompositionEvent_h -#define CompositionEvent_h +#pragma once #include "UIEvent.h" namespace WebCore { -struct CompositionEventInit : UIEventInit { - CompositionEventInit(); - - String data; -}; - -class CompositionEvent : public UIEvent { +class CompositionEvent final : public UIEvent { public: - static PassRefPtr<CompositionEvent> create() + static Ref<CompositionEvent> create(const AtomicString& type, DOMWindow* view, const String& data) { - return adoptRef(new CompositionEvent); + return adoptRef(*new CompositionEvent(type, view, data)); } - static PassRefPtr<CompositionEvent> create(const AtomicString& type, PassRefPtr<AbstractView> view, const String& data) + static Ref<CompositionEvent> createForBindings() { - return adoptRef(new CompositionEvent(type, view, data)); + return adoptRef(*new CompositionEvent); } - static PassRefPtr<CompositionEvent> create(const AtomicString& type, const CompositionEventInit& initializer) + struct Init : UIEventInit { + String data; + }; + + static Ref<CompositionEvent> create(const AtomicString& type, const Init& initializer, IsTrusted isTrusted = IsTrusted::No) { - return adoptRef(new CompositionEvent(type, initializer)); + return adoptRef(*new CompositionEvent(type, initializer, isTrusted)); } virtual ~CompositionEvent(); - void initCompositionEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<AbstractView>, const String& data); + void initCompositionEvent(const AtomicString& type, bool canBubble, bool cancelable, DOMWindow*, const String& data); String data() const { return m_data; } - virtual EventInterface eventInterface() const override; + EventInterface eventInterface() const override; private: CompositionEvent(); - CompositionEvent(const AtomicString& type, PassRefPtr<AbstractView>, const String&); - CompositionEvent(const AtomicString& type, const CompositionEventInit&); + CompositionEvent(const AtomicString& type, DOMWindow*, const String&); + CompositionEvent(const AtomicString& type, const Init&, IsTrusted); + + bool isCompositionEvent() const override; String m_data; }; } // namespace WebCore -#endif // CompositionEvent_h +SPECIALIZE_TYPE_TRAITS_EVENT(CompositionEvent) diff --git a/Source/WebCore/dom/CompositionEvent.idl b/Source/WebCore/dom/CompositionEvent.idl index 05ace6fdd..d87240883 100644 --- a/Source/WebCore/dom/CompositionEvent.idl +++ b/Source/WebCore/dom/CompositionEvent.idl @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,17 +24,20 @@ */ [ - ConstructorConditional=DOM4_EVENTS_CONSTRUCTOR, - ConstructorTemplate=Event, + Constructor(DOMString type, optional CompositionEventInit eventInitDict), ] interface CompositionEvent : UIEvent { - [InitializedByEventConstructor] readonly attribute DOMString data; + readonly attribute DOMString data; - void initCompositionEvent([Default=Undefined] optional DOMString typeArg, - [Default=Undefined] optional boolean canBubbleArg, - [Default=Undefined] optional boolean cancelableArg, - [Default=Undefined] optional DOMWindow viewArg, - [Default=Undefined] optional DOMString dataArg); + // FIXME: Using "undefined" as default parameter value is wrong. + void initCompositionEvent(optional DOMString typeArg = "undefined", + optional boolean canBubbleArg = false, + optional boolean cancelableArg = false, + optional DOMWindow? viewArg = null, + optional DOMString dataArg = "undefined"); }; +dictionary CompositionEventInit : UIEventInit { + DOMString data = ""; +}; diff --git a/Source/WebCore/dom/ContainerNode.cpp b/Source/WebCore/dom/ContainerNode.cpp index 44a4c3a14..e9ed30dd7 100644 --- a/Source/WebCore/dom/ContainerNode.cpp +++ b/Source/WebCore/dom/ContainerNode.cpp @@ -2,7 +2,7 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2016 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -24,71 +24,71 @@ #include "ContainerNode.h" #include "AXObjectCache.h" +#include "AllDescendantsCollection.h" #include "ChildListMutationScope.h" -#include "Chrome.h" -#include "ChromeClient.h" -#include "ClassNodeList.h" +#include "ClassCollection.h" +#include "CommonVM.h" #include "ContainerNodeAlgorithms.h" #include "Editor.h" +#include "EventNames.h" #include "FloatRect.h" #include "FrameView.h" +#include "GenericCachedHTMLCollection.h" +#include "HTMLFormControlsCollection.h" +#include "HTMLOptionsCollection.h" +#include "HTMLSlotElement.h" +#include "HTMLTableRowsCollection.h" #include "InlineTextBox.h" -#include "InsertionPoint.h" -#include "JSLazyEventListener.h" +#include "InspectorInstrumentation.h" #include "JSNode.h" #include "LabelsNodeList.h" -#include "LoaderStrategy.h" -#include "MemoryCache.h" #include "MutationEvent.h" #include "NameNodeList.h" +#include "NoEventDispatchAssertion.h" #include "NodeRareData.h" #include "NodeRenderStyle.h" -#include "PlatformStrategies.h" #include "RadioNodeList.h" #include "RenderBox.h" #include "RenderTheme.h" +#include "RenderTreeUpdater.h" #include "RenderWidget.h" -#include "ResourceLoadScheduler.h" #include "RootInlineBox.h" +#include "RuntimeEnabledFeatures.h" +#include "SVGDocumentExtensions.h" +#include "SVGElement.h" +#include "SVGNames.h" +#include "SVGUseElement.h" #include "SelectorQuery.h" #include "TemplateContentDocumentFragment.h" -#include <wtf/CurrentTime.h> - -#if ENABLE(DELETION_UI) -#include "DeleteButtonController.h" -#endif +#include <algorithm> +#include <wtf/Variant.h> namespace WebCore { static void dispatchChildInsertionEvents(Node&); static void dispatchChildRemovalEvents(Node&); -typedef std::pair<RefPtr<Node>, unsigned> CallbackParameters; -typedef std::pair<NodeCallback, CallbackParameters> CallbackInfo; -typedef Vector<CallbackInfo> NodeCallbackQueue; - -static NodeCallbackQueue* s_postAttachCallbackQueue; - -static size_t s_attachDepth; -static bool s_shouldReEnableMemoryCacheCallsAfterAttach; +ChildNodesLazySnapshot* ChildNodesLazySnapshot::latestSnapshot; -ChildNodesLazySnapshot* ChildNodesLazySnapshot::latestSnapshot = 0; - -#ifndef NDEBUG +#if !ASSERT_DISABLED unsigned NoEventDispatchAssertion::s_count = 0; +unsigned NoEventDispatchAssertion::DisableAssertionsInScope::s_existingCount = 0; +NoEventDispatchAssertion::EventAllowedScope* NoEventDispatchAssertion::EventAllowedScope::s_currentScope = nullptr; #endif -static void collectChildrenAndRemoveFromOldParent(Node& node, NodeVector& nodes, ExceptionCode& ec) +static ExceptionOr<void> collectChildrenAndRemoveFromOldParent(Node& node, NodeVector& nodes) { - if (!node.isDocumentFragment()) { + if (!is<DocumentFragment>(node)) { nodes.append(node); - if (ContainerNode* oldParent = node.parentNode()) - oldParent->removeChild(&node, ec); - return; + auto* oldParent = node.parentNode(); + if (!oldParent) + return { }; + return oldParent->removeChild(node); } getChildNodes(node, nodes); - toContainerNode(node).removeChildren(); + downcast<DocumentFragment>(node).removeChildren(); + return { }; } // FIXME: This function must get a new name. @@ -101,18 +101,21 @@ void ContainerNode::removeDetachedChildren() child->updateAncestorConnectedSubframeCountForRemoval(); } // FIXME: We should be able to ASSERT(!attached()) here: https://bugs.webkit.org/show_bug.cgi?id=107801 - removeDetachedChildrenInContainer<Node, ContainerNode>(*this); + removeDetachedChildrenInContainer(*this); } static inline void destroyRenderTreeIfNeeded(Node& child) { + bool isElement = is<Element>(child); + auto hasDisplayContents = isElement && downcast<Element>(child).hasDisplayContents(); + auto isNamedFlowElement = isElement && downcast<Element>(child).isNamedFlowContentElement(); // FIXME: Get rid of the named flow test. - if (!child.renderer() && !child.inNamedFlow()) + if (!child.renderer() && !hasDisplayContents && !isNamedFlowElement) return; - if (child.isElementNode()) - Style::detachRenderTree(toElement(child)); - else if (child.isTextNode()) - Style::detachTextRenderer(toText(child)); + if (isElement) + RenderTreeUpdater::tearDownRenderers(downcast<Element>(child)); + else if (is<Text>(child)) + RenderTreeUpdater::tearDownRenderer(downcast<Text>(child)); } void ContainerNode::takeAllChildrenFrom(ContainerNode* oldParent) @@ -124,44 +127,48 @@ void ContainerNode::takeAllChildrenFrom(ContainerNode* oldParent) if (oldParent->document().hasMutationObserversOfType(MutationObserver::ChildList)) { ChildListMutationScope mutation(*oldParent); - for (unsigned i = 0; i < children.size(); ++i) - mutation.willRemoveChild(children[i].get()); + for (auto& child : children) + mutation.willRemoveChild(child); } - // FIXME: We need to do notifyMutationObserversNodeWillDetach() for each child, - // probably inside removeDetachedChildrenInContainer. - - oldParent->removeDetachedChildren(); + disconnectSubframesIfNeeded(*oldParent, DescendantsOnly); + { + NoEventDispatchAssertion assertNoEventDispatch; - for (unsigned i = 0; i < children.size(); ++i) { - Node& child = children[i].get(); + oldParent->document().nodeChildrenWillBeRemoved(*oldParent); - destroyRenderTreeIfNeeded(child); + WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates; + while (RefPtr<Node> child = oldParent->m_firstChild) { + oldParent->removeBetween(nullptr, child->nextSibling(), *child); + notifyChildNodeRemoved(*oldParent, *child); + } + ChildChange change = { AllChildrenRemoved, nullptr, nullptr, ChildChangeSourceParser }; + childrenChanged(change); + } - // FIXME: We need a no mutation event version of adoptNode. - RefPtr<Node> adoptedChild = document().adoptNode(&children[i].get(), ASSERT_NO_EXCEPTION); - parserAppendChild(adoptedChild.get()); - // FIXME: Together with adoptNode above, the tree scope might get updated recursively twice - // (if the document changed or oldParent was in a shadow tree, AND *this is in a shadow tree). - // Can we do better? - treeScope().adoptIfNeeded(adoptedChild.get()); + // FIXME: assert that we don't dispatch events here since this container node is still disconnected. + for (auto& child : children) { + RELEASE_ASSERT(!child->parentNode() && &child->treeScope() == &treeScope()); + ASSERT(!ensurePreInsertionValidity(child, nullptr).hasException()); + treeScope().adoptIfNeeded(child); + parserAppendChild(child); } } ContainerNode::~ContainerNode() { - if (Document* document = documentInternal()) - willBeDeletedFrom(document); + if (!isDocumentNode()) + willBeDeletedFrom(document()); removeDetachedChildren(); } -static inline bool isChildTypeAllowed(ContainerNode* newParent, Node* child) +static inline bool isChildTypeAllowed(ContainerNode& newParent, Node& child) { - if (!child->isDocumentFragment()) - return newParent->childTypeAllowed(child->nodeType()); + if (!child.isDocumentFragment()) + return newParent.childTypeAllowed(child.nodeType()); - for (Node* node = child->firstChild(); node; node = node->nextSibling()) { - if (!newParent->childTypeAllowed(node->nodeType())) + for (Node* node = child.firstChild(); node; node = node->nextSibling()) { + if (!newParent.childTypeAllowed(node->nodeType())) return false; } return true; @@ -169,152 +176,129 @@ static inline bool isChildTypeAllowed(ContainerNode* newParent, Node* child) static inline bool isInTemplateContent(const Node* node) { -#if ENABLE(TEMPLATE_ELEMENT) Document& document = node->document(); return &document == document.templateDocument(); -#else - UNUSED_PARAM(node); - return false; -#endif } -static inline bool containsConsideringHostElements(const Node* newChild, const Node* newParent) +static inline bool containsConsideringHostElements(const Node& newChild, const Node& newParent) { - return (newParent->isInShadowTree() || isInTemplateContent(newParent)) - ? newChild->containsIncludingHostElements(newParent) - : newChild->contains(newParent); + return (newParent.isInShadowTree() || isInTemplateContent(&newParent)) + ? newChild.containsIncludingHostElements(&newParent) + : newChild.contains(&newParent); } -static inline ExceptionCode checkAcceptChild(ContainerNode* newParent, Node* newChild, Node* oldChild) +static inline ExceptionOr<void> checkAcceptChild(ContainerNode& newParent, Node& newChild, const Node* refChild, Document::AcceptChildOperation operation) { - // Not mentioned in spec: throw NOT_FOUND_ERR if newChild is null - if (!newChild) - return NOT_FOUND_ERR; - // Use common case fast path if possible. - if ((newChild->isElementNode() || newChild->isTextNode()) && newParent->isElementNode()) { - ASSERT(!newParent->isReadOnlyNode()); - ASSERT(!newParent->isDocumentTypeNode()); + if ((newChild.isElementNode() || newChild.isTextNode()) && newParent.isElementNode()) { + ASSERT(!newParent.isDocumentTypeNode()); ASSERT(isChildTypeAllowed(newParent, newChild)); if (containsConsideringHostElements(newChild, newParent)) - return HIERARCHY_REQUEST_ERR; - return 0; + return Exception { HIERARCHY_REQUEST_ERR }; + if (operation == Document::AcceptChildOperation::InsertOrAdd && refChild && refChild->parentNode() != &newParent) + return Exception { NOT_FOUND_ERR }; + return { }; } // This should never happen, but also protect release builds from tree corruption. - ASSERT(!newChild->isPseudoElement()); - if (newChild->isPseudoElement()) - return HIERARCHY_REQUEST_ERR; + ASSERT(!newChild.isPseudoElement()); + if (newChild.isPseudoElement()) + return Exception { HIERARCHY_REQUEST_ERR }; - if (newParent->isReadOnlyNode()) - return NO_MODIFICATION_ALLOWED_ERR; if (containsConsideringHostElements(newChild, newParent)) - return HIERARCHY_REQUEST_ERR; + return Exception { HIERARCHY_REQUEST_ERR }; + + if (operation == Document::AcceptChildOperation::InsertOrAdd && refChild && refChild->parentNode() != &newParent) + return Exception { NOT_FOUND_ERR }; - if (oldChild && newParent->isDocumentNode()) { - if (!toDocument(newParent)->canReplaceChild(newChild, oldChild)) - return HIERARCHY_REQUEST_ERR; + if (is<Document>(newParent)) { + if (!downcast<Document>(newParent).canAcceptChild(newChild, refChild, operation)) + return Exception { HIERARCHY_REQUEST_ERR }; } else if (!isChildTypeAllowed(newParent, newChild)) - return HIERARCHY_REQUEST_ERR; + return Exception { HIERARCHY_REQUEST_ERR }; - return 0; + return { }; } -static inline bool checkAcceptChildGuaranteedNodeTypes(ContainerNode* newParent, Node* newChild, ExceptionCode& ec) +static inline ExceptionOr<void> checkAcceptChildGuaranteedNodeTypes(ContainerNode& newParent, Node& newChild) { - ASSERT(!newParent->isReadOnlyNode()); - ASSERT(!newParent->isDocumentTypeNode()); + ASSERT(!newParent.isDocumentTypeNode()); ASSERT(isChildTypeAllowed(newParent, newChild)); - if (newChild->contains(newParent)) { - ec = HIERARCHY_REQUEST_ERR; - return false; - } - - return true; + if (newChild.contains(&newParent)) + return Exception { HIERARCHY_REQUEST_ERR }; + return { }; } -static inline bool checkAddChild(ContainerNode* newParent, Node* newChild, ExceptionCode& ec) +// https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity +ExceptionOr<void> ContainerNode::ensurePreInsertionValidity(Node& newChild, Node* refChild) { - ec = checkAcceptChild(newParent, newChild, 0); - if (ec) - return false; - - return true; + return checkAcceptChild(*this, newChild, refChild, Document::AcceptChildOperation::InsertOrAdd); } -static inline bool checkReplaceChild(ContainerNode* newParent, Node* newChild, Node* oldChild, ExceptionCode& ec) +// https://dom.spec.whatwg.org/#concept-node-replace +static inline ExceptionOr<void> checkPreReplacementValidity(ContainerNode& newParent, Node& newChild, Node& oldChild) { - ec = checkAcceptChild(newParent, newChild, oldChild); - if (ec) - return false; - - return true; + return checkAcceptChild(newParent, newChild, &oldChild, Document::AcceptChildOperation::Replace); } -bool ContainerNode::insertBefore(PassRefPtr<Node> newChild, Node* refChild, ExceptionCode& ec) +ExceptionOr<void> ContainerNode::insertBefore(Node& newChild, Node* refChild) { // Check that this node is not "floating". // If it is, it can be deleted as a side effect of sending mutation events. ASSERT(refCount() || parentOrShadowHostNode()); - Ref<ContainerNode> protect(*this); - - ec = 0; - - // insertBefore(node, 0) is equivalent to appendChild(node) - if (!refChild) - return appendChild(newChild, ec); - // Make sure adding the new child is OK. - if (!checkAddChild(this, newChild.get(), ec)) - return false; + auto validityCheckResult = ensurePreInsertionValidity(newChild, refChild); + if (validityCheckResult.hasException()) + return validityCheckResult.releaseException(); - // NOT_FOUND_ERR: Raised if refChild is not a child of this node - if (refChild->parentNode() != this) { - ec = NOT_FOUND_ERR; - return false; - } + if (refChild == &newChild) + refChild = newChild.nextSibling(); - if (refChild->previousSibling() == newChild || refChild == newChild) // nothing to do - return true; + // insertBefore(node, null) is equivalent to appendChild(node) + if (!refChild) + return appendChildWithoutPreInsertionValidityCheck(newChild); + Ref<ContainerNode> protectedThis(*this); Ref<Node> next(*refChild); NodeVector targets; - collectChildrenAndRemoveFromOldParent(*newChild.get(), targets, ec); - if (ec) - return false; + auto removeResult = collectChildrenAndRemoveFromOldParent(newChild, targets); + if (removeResult.hasException()) + return removeResult.releaseException(); if (targets.isEmpty()) - return true; + return { }; // We need this extra check because collectChildrenAndRemoveFromOldParent() can fire mutation events. - if (!checkAcceptChildGuaranteedNodeTypes(this, newChild.get(), ec)) - return false; + auto checkAcceptResult = checkAcceptChildGuaranteedNodeTypes(*this, newChild); + if (checkAcceptResult.hasException()) + return checkAcceptResult.releaseException(); - InspectorInstrumentation::willInsertDOMNode(&document(), this); + InspectorInstrumentation::willInsertDOMNode(document(), *this); ChildListMutationScope mutation(*this); - for (auto it = targets.begin(), end = targets.end(); it != end; ++it) { - Node& child = it->get(); - + for (auto& child : targets) { // Due to arbitrary code running in response to a DOM mutation event it's // possible that "next" is no longer a child of "this". // It's also possible that "child" has been inserted elsewhere. // In either of those cases, we'll just stop. if (next->parentNode() != this) break; - if (child.parentNode()) + if (child->parentNode()) break; - treeScope().adoptIfNeeded(&child); + { + NoEventDispatchAssertion assertNoEventDispatch; - insertBeforeCommon(next.get(), child); + treeScope().adoptIfNeeded(child); + insertBeforeCommon(next, child); + } updateTreeAfterInsertion(child); } dispatchSubtreeModifiedEvent(); - return true; + return { }; } void ContainerNode::insertBeforeCommon(Node& nextChild, Node& newChild) @@ -342,145 +326,161 @@ void ContainerNode::insertBeforeCommon(Node& nextChild, Node& newChild) newChild.setNextSibling(&nextChild); } -void ContainerNode::notifyChildInserted(Node& child, ChildChangeSource source) +void ContainerNode::appendChildCommon(Node& child) { - ChildChange change; - change.type = child.isElementNode() ? ElementInserted : child.isTextNode() ? TextInserted : NonContentsChildChanged; - change.previousSiblingElement = ElementTraversal::previousSibling(&child); - change.nextSiblingElement = ElementTraversal::nextSibling(&child); - change.source = source; + NoEventDispatchAssertion assertNoEventDispatch; + + child.setParentNode(this); + + if (m_lastChild) { + child.setPreviousSibling(m_lastChild); + m_lastChild->setNextSibling(&child); + } else + m_firstChild = &child; + + m_lastChild = &child; +} + +inline auto ContainerNode::changeForChildInsertion(Node& child, ChildChangeSource source, ReplacedAllChildren replacedAllChildren) -> ChildChange +{ + if (replacedAllChildren == ReplacedAllChildren::Yes) + return { AllChildrenReplaced, nullptr, nullptr, source }; + + return { + child.isElementNode() ? ElementInserted : child.isTextNode() ? TextInserted : NonContentsChildInserted, + ElementTraversal::previousSibling(child), + ElementTraversal::nextSibling(child), + source + }; +} + +void ContainerNode::notifyChildInserted(Node& child, const ChildChange& change) +{ + ChildListMutationScope(*this).childAdded(child); + + NodeVector postInsertionNotificationTargets; + notifyChildNodeInserted(*this, child, postInsertionNotificationTargets); childrenChanged(change); + + for (auto& target : postInsertionNotificationTargets) + target->finishedInsertingSubtree(); } void ContainerNode::notifyChildRemoved(Node& child, Node* previousSibling, Node* nextSibling, ChildChangeSource source) { + NoEventDispatchAssertion assertNoEventDispatch; + notifyChildNodeRemoved(*this, child); + ChildChange change; - change.type = child.isElementNode() ? ElementRemoved : child.isTextNode() ? TextRemoved : NonContentsChildChanged; - change.previousSiblingElement = (!previousSibling || previousSibling->isElementNode()) ? toElement(previousSibling) : ElementTraversal::previousSibling(previousSibling); - change.nextSiblingElement = (!nextSibling || nextSibling->isElementNode()) ? toElement(nextSibling) : ElementTraversal::nextSibling(nextSibling); + change.type = is<Element>(child) ? ElementRemoved : is<Text>(child) ? TextRemoved : NonContentsChildRemoved; + change.previousSiblingElement = (!previousSibling || is<Element>(*previousSibling)) ? downcast<Element>(previousSibling) : ElementTraversal::previousSibling(*previousSibling); + change.nextSiblingElement = (!nextSibling || is<Element>(*nextSibling)) ? downcast<Element>(nextSibling) : ElementTraversal::nextSibling(*nextSibling); change.source = source; childrenChanged(change); } -void ContainerNode::parserInsertBefore(PassRefPtr<Node> newChild, Node* nextChild) +void ContainerNode::parserInsertBefore(Node& newChild, Node& nextChild) { - ASSERT(newChild); - ASSERT(nextChild); - ASSERT(nextChild->parentNode() == this); - ASSERT(!newChild->isDocumentFragment()); -#if ENABLE(TEMPLATE_ELEMENT) + ASSERT(nextChild.parentNode() == this); + ASSERT(!newChild.isDocumentFragment()); ASSERT(!hasTagName(HTMLNames::templateTag)); -#endif - if (nextChild->previousSibling() == newChild || nextChild == newChild) // nothing to do + if (nextChild.previousSibling() == &newChild || &nextChild == &newChild) // nothing to do return; - if (&document() != &newChild->document()) - document().adoptNode(newChild.get(), ASSERT_NO_EXCEPTION); - - insertBeforeCommon(*nextChild, *newChild.get()); + if (&document() != &newChild.document()) + document().adoptNode(newChild); - newChild->updateAncestorConnectedSubframeCountForInsertion(); + insertBeforeCommon(nextChild, newChild); - ChildListMutationScope(*this).childAdded(*newChild); + newChild.updateAncestorConnectedSubframeCountForInsertion(); - notifyChildInserted(*newChild, ChildChangeSourceParser); - - ChildNodeInsertionNotifier(*this).notify(*newChild); - - newChild->setNeedsStyleRecalc(ReconstructRenderTree); + notifyChildInserted(newChild, changeForChildInsertion(newChild, ChildChangeSourceParser)); } -bool ContainerNode::replaceChild(PassRefPtr<Node> newChild, Node* oldChild, ExceptionCode& ec) +ExceptionOr<void> ContainerNode::replaceChild(Node& newChild, Node& oldChild) { // Check that this node is not "floating". // If it is, it can be deleted as a side effect of sending mutation events. ASSERT(refCount() || parentOrShadowHostNode()); - Ref<ContainerNode> protect(*this); - - ec = 0; - - if (oldChild == newChild) // nothing to do - return true; - - if (!oldChild) { - ec = NOT_FOUND_ERR; - return false; - } + Ref<ContainerNode> protectedThis(*this); // Make sure replacing the old child with the new is ok - if (!checkReplaceChild(this, newChild.get(), oldChild, ec)) - return false; + auto validityResult = checkPreReplacementValidity(*this, newChild, oldChild); + if (validityResult.hasException()) + return validityResult.releaseException(); // NOT_FOUND_ERR: Raised if oldChild is not a child of this node. - if (oldChild->parentNode() != this) { - ec = NOT_FOUND_ERR; - return false; - } + if (oldChild.parentNode() != this) + return Exception { NOT_FOUND_ERR }; - ChildListMutationScope mutation(*this); - - RefPtr<Node> next = oldChild->nextSibling(); + RefPtr<Node> refChild = oldChild.nextSibling(); + if (refChild.get() == &newChild) + refChild = refChild->nextSibling(); - // Remove the node we're replacing - Ref<Node> removedChild(*oldChild); - removeChild(oldChild, ec); - if (ec) - return false; + NodeVector targets; + { + ChildListMutationScope mutation(*this); + auto collectResult = collectChildrenAndRemoveFromOldParent(newChild, targets); + if (collectResult.hasException()) + return collectResult.releaseException(); + } - if (next && (next->previousSibling() == newChild || next == newChild)) // nothing to do - return true; + // Do this one more time because collectChildrenAndRemoveFromOldParent() fires a MutationEvent. + validityResult = checkPreReplacementValidity(*this, newChild, oldChild); + if (validityResult.hasException()) + return validityResult.releaseException(); - // Does this one more time because removeChild() fires a MutationEvent. - if (!checkReplaceChild(this, newChild.get(), oldChild, ec)) - return false; + // Remove the node we're replacing. + Ref<Node> protectOldChild(oldChild); - NodeVector targets; - collectChildrenAndRemoveFromOldParent(*newChild.get(), targets, ec); - if (ec) - return false; + ChildListMutationScope mutation(*this); - // Does this yet another check because collectChildrenAndRemoveFromOldParent() fires a MutationEvent. - if (!checkReplaceChild(this, newChild.get(), oldChild, ec)) - return false; + // If oldChild == newChild then oldChild no longer has a parent at this point. + if (oldChild.parentNode()) { + auto removeResult = removeChild(oldChild); + if (removeResult.hasException()) + return removeResult.releaseException(); - InspectorInstrumentation::willInsertDOMNode(&document(), this); + // Does this one more time because removeChild() fires a MutationEvent. + validityResult = checkPreReplacementValidity(*this, newChild, oldChild); + if (validityResult.hasException()) + return validityResult.releaseException(); + } - // Add the new child(ren) - for (auto it = targets.begin(), end = targets.end(); it != end; ++it) { - Node& child = it->get(); + InspectorInstrumentation::willInsertDOMNode(document(), *this); + // Add the new child(ren). + for (auto& child : targets) { // Due to arbitrary code running in response to a DOM mutation event it's - // possible that "next" is no longer a child of "this". + // possible that "refChild" is no longer a child of "this". // It's also possible that "child" has been inserted elsewhere. // In either of those cases, we'll just stop. - if (next && next->parentNode() != this) + if (refChild && refChild->parentNode() != this) break; - if (child.parentNode()) + if (child->parentNode()) break; - treeScope().adoptIfNeeded(&child); - - // Add child before "next". { NoEventDispatchAssertion assertNoEventDispatch; - if (next) - insertBeforeCommon(*next, child); + treeScope().adoptIfNeeded(child); + if (refChild) + insertBeforeCommon(*refChild, child.get()); else - appendChildToContainer(&child, *this); + appendChildCommon(child); } - updateTreeAfterInsertion(child); + updateTreeAfterInsertion(child.get()); } dispatchSubtreeModifiedEvent(); - return true; + return { }; } -void ContainerNode::willRemoveChild(Node& child) +static void willRemoveChild(ContainerNode& container, Node& child) { ASSERT(child.parentNode()); @@ -488,12 +488,11 @@ void ContainerNode::willRemoveChild(Node& child) child.notifyMutationObserversNodeWillDetach(); dispatchChildRemovalEvents(child); - if (child.parentNode() != this) + if (child.parentNode() != &container) return; - child.document().nodeWillBeRemoved(&child); // e.g. mutation event listener can create a new range. - if (child.isContainerNode()) - disconnectSubframesIfNeeded(toContainerNode(child), RootAndDescendants); + if (is<ContainerNode>(child)) + disconnectSubframesIfNeeded(downcast<ContainerNode>(child), RootAndDescendants); } static void willRemoveChildren(ContainerNode& container) @@ -502,17 +501,14 @@ static void willRemoveChildren(ContainerNode& container) getChildNodes(container, children); ChildListMutationScope mutation(container); - for (auto it = children.begin(); it != children.end(); ++it) { - Node& child = it->get(); - mutation.willRemoveChild(child); - child.notifyMutationObserversNodeWillDetach(); + for (auto& child : children) { + mutation.willRemoveChild(child.get()); + child->notifyMutationObserversNodeWillDetach(); // fire removed from document mutation events. - dispatchChildRemovalEvents(child); + dispatchChildRemovalEvents(child.get()); } - container.document().nodeChildrenWillBeRemoved(container); - disconnectSubframesIfNeeded(container, DescendantsOnly); } @@ -521,70 +517,48 @@ void ContainerNode::disconnectDescendantFrames() disconnectSubframesIfNeeded(*this, RootAndDescendants); } -bool ContainerNode::removeChild(Node* oldChild, ExceptionCode& ec) +ExceptionOr<void> ContainerNode::removeChild(Node& oldChild) { // Check that this node is not "floating". // If it is, it can be deleted as a side effect of sending mutation events. ASSERT(refCount() || parentOrShadowHostNode()); - Ref<ContainerNode> protect(*this); - - ec = 0; - - // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. - if (isReadOnlyNode()) { - ec = NO_MODIFICATION_ALLOWED_ERR; - return false; - } + Ref<ContainerNode> protectedThis(*this); // NOT_FOUND_ERR: Raised if oldChild is not a child of this node. - if (!oldChild || oldChild->parentNode() != this) { - ec = NOT_FOUND_ERR; - return false; - } - - Ref<Node> child(*oldChild); - - document().removeFocusedNodeOfSubtree(&child.get()); + if (oldChild.parentNode() != this) + return Exception { NOT_FOUND_ERR }; -#if ENABLE(FULLSCREEN_API) - document().removeFullScreenElementOfSubtree(&child.get()); -#endif - - // Events fired when blurring currently focused node might have moved this - // child into a different parent. - if (child->parentNode() != this) { - ec = NOT_FOUND_ERR; - return false; - } + Ref<Node> child(oldChild); - willRemoveChild(child.get()); + willRemoveChild(*this, child); - // Mutation events might have moved this child into a different parent. - if (child->parentNode() != this) { - ec = NOT_FOUND_ERR; - return false; - } + // Mutation events in willRemoveChild might have moved this child into a different parent. + if (child->parentNode() != this) + return Exception { NOT_FOUND_ERR }; { WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates; + NoEventDispatchAssertion assertNoEventDispatch; + + document().nodeWillBeRemoved(child); Node* prev = child->previousSibling(); Node* next = child->nextSibling(); - removeBetween(prev, next, child.get()); + removeBetween(prev, next, child); - notifyChildRemoved(child.get(), prev, next, ChildChangeSourceAPI); - - ChildNodeRemovalNotifier(*this).notify(child.get()); + notifyChildRemoved(child, prev, next, ChildChangeSourceAPI); } + + rebuildSVGExtensionsElementsIfNecessary(); dispatchSubtreeModifiedEvent(); - return true; + return { }; } void ContainerNode::removeBetween(Node* previousChild, Node* nextChild, Node& oldChild) { - InspectorInstrumentation::didRemoveDOMNode(&oldChild.document(), &oldChild); + InspectorInstrumentation::didRemoveDOMNode(oldChild.document(), oldChild); NoEventDispatchAssertion assertNoEventDispatch; @@ -592,40 +566,116 @@ void ContainerNode::removeBetween(Node* previousChild, Node* nextChild, Node& ol destroyRenderTreeIfNeeded(oldChild); - if (nextChild) + if (nextChild) { nextChild->setPreviousSibling(previousChild); - if (previousChild) + oldChild.setNextSibling(nullptr); + } else { + ASSERT(m_lastChild == &oldChild); + m_lastChild = previousChild; + } + if (previousChild) { previousChild->setNextSibling(nextChild); - if (m_firstChild == &oldChild) + oldChild.setPreviousSibling(nullptr); + } else { + ASSERT(m_firstChild == &oldChild); m_firstChild = nextChild; - if (m_lastChild == &oldChild) - m_lastChild = previousChild; + } - oldChild.setPreviousSibling(0); - oldChild.setNextSibling(0); - oldChild.setParentNode(0); + ASSERT(m_firstChild != &oldChild); + ASSERT(m_lastChild != &oldChild); + ASSERT(!oldChild.previousSibling()); + ASSERT(!oldChild.nextSibling()); + oldChild.setParentNode(nullptr); - document().adoptIfNeeded(&oldChild); + document().adoptIfNeeded(oldChild); } void ContainerNode::parserRemoveChild(Node& oldChild) { - ASSERT(oldChild.parentNode() == this); - ASSERT(!oldChild.isDocumentFragment()); + disconnectSubframesIfNeeded(*this, DescendantsOnly); + if (oldChild.parentNode() != this) + return; - Node* prev = oldChild.previousSibling(); - Node* next = oldChild.nextSibling(); + { + NoEventDispatchAssertion assertNoEventDispatch; - oldChild.updateAncestorConnectedSubframeCountForRemoval(); + document().nodeChildrenWillBeRemoved(*this); - ChildListMutationScope(*this).willRemoveChild(oldChild); - oldChild.notifyMutationObserversNodeWillDetach(); + ASSERT(oldChild.parentNode() == this); + ASSERT(!oldChild.isDocumentFragment()); - removeBetween(prev, next, oldChild); + Node* prev = oldChild.previousSibling(); + Node* next = oldChild.nextSibling(); - notifyChildRemoved(oldChild, prev, next, ChildChangeSourceParser); + ChildListMutationScope(*this).willRemoveChild(oldChild); + oldChild.notifyMutationObserversNodeWillDetach(); - ChildNodeRemovalNotifier(*this).notify(oldChild); + removeBetween(prev, next, oldChild); + + notifyChildRemoved(oldChild, prev, next, ChildChangeSourceParser); + } +} + +// https://dom.spec.whatwg.org/#concept-node-replace-all +void ContainerNode::replaceAllChildren(std::nullptr_t) +{ + ChildListMutationScope mutation(*this); + removeChildren(); +} + +// https://dom.spec.whatwg.org/#concept-node-replace-all +void ContainerNode::replaceAllChildren(Ref<Node>&& node) +{ + // This function assumes the input node is not a DocumentFragment and is parentless to decrease complexity. + ASSERT(!is<DocumentFragment>(node)); + ASSERT(!node->parentNode()); + + if (!hasChildNodes()) { + // appendChildWithoutPreInsertionValidityCheck() can only throw when node has a parent and we already asserted it doesn't. + auto result = appendChildWithoutPreInsertionValidityCheck(node); + ASSERT_UNUSED(result, !result.hasException()); + return; + } + + Ref<ContainerNode> protectedThis(*this); + ChildListMutationScope mutation(*this); + + // If node is not null, adopt node into parent's node document. + treeScope().adoptIfNeeded(node); + + // Remove all parent's children, in tree order. + willRemoveChildren(*this); + + { + WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates; + { + NoEventDispatchAssertion assertNoEventDispatch; + + document().nodeChildrenWillBeRemoved(*this); + + while (RefPtr<Node> child = m_firstChild) { + removeBetween(nullptr, child->nextSibling(), *child); + notifyChildNodeRemoved(*this, *child); + } + + // If node is not null, insert node into parent before null. + ASSERT(!ensurePreInsertionValidity(node, nullptr).hasException()); + InspectorInstrumentation::willInsertDOMNode(document(), *this); + + appendChildCommon(node); + } + + updateTreeAfterInsertion(node, ReplacedAllChildren::Yes); + } + + rebuildSVGExtensionsElementsIfNecessary(); + dispatchSubtreeModifiedEvent(); +} + +inline void ContainerNode::rebuildSVGExtensionsElementsIfNecessary() +{ + if (document().svgExtensions() && !is<SVGUseElement>(shadowHost())) + document().accessSVGExtensions().rebuildElements(); } // this differs from other remove functions because it forcibly removes all the children, @@ -636,187 +686,106 @@ void ContainerNode::removeChildren() return; // The container node can be removed from event handlers. - Ref<ContainerNode> protect(*this); - - // exclude this node when looking for removed focusedNode since only children will be removed - document().removeFocusedNodeOfSubtree(this, true); - -#if ENABLE(FULLSCREEN_API) - document().removeFullScreenElementOfSubtree(this, true); -#endif + Ref<ContainerNode> protectedThis(*this); // Do any prep work needed before actually starting to detach // and remove... e.g. stop loading frames, fire unload events. willRemoveChildren(*this); - NodeVector removedChildren; { WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates; - { - NoEventDispatchAssertion assertNoEventDispatch; - removedChildren.reserveInitialCapacity(childNodeCount()); - while (RefPtr<Node> n = m_firstChild) { - removedChildren.append(*m_firstChild); - removeBetween(0, m_firstChild->nextSibling(), *m_firstChild); - } + NoEventDispatchAssertion assertNoEventDispatch; + + document().nodeChildrenWillBeRemoved(*this); + + while (RefPtr<Node> child = m_firstChild) { + removeBetween(0, child->nextSibling(), *child); + notifyChildNodeRemoved(*this, *child); } ChildChange change = { AllChildrenRemoved, nullptr, nullptr, ChildChangeSourceAPI }; childrenChanged(change); - - for (size_t i = 0; i < removedChildren.size(); ++i) - ChildNodeRemovalNotifier(*this).notify(removedChildren[i].get()); } + rebuildSVGExtensionsElementsIfNecessary(); dispatchSubtreeModifiedEvent(); } -bool ContainerNode::appendChild(PassRefPtr<Node> newChild, ExceptionCode& ec) +ExceptionOr<void> ContainerNode::appendChild(Node& newChild) { - Ref<ContainerNode> protect(*this); - // Check that this node is not "floating". // If it is, it can be deleted as a side effect of sending mutation events. ASSERT(refCount() || parentOrShadowHostNode()); - ec = 0; - // Make sure adding the new child is ok - if (!checkAddChild(this, newChild.get(), ec)) - return false; + auto validityCheckResult = ensurePreInsertionValidity(newChild, nullptr); + if (validityCheckResult.hasException()) + return validityCheckResult.releaseException(); + + return appendChildWithoutPreInsertionValidityCheck(newChild); +} - if (newChild == m_lastChild) // nothing to do - return newChild; +ExceptionOr<void> ContainerNode::appendChildWithoutPreInsertionValidityCheck(Node& newChild) +{ + Ref<ContainerNode> protectedThis(*this); NodeVector targets; - collectChildrenAndRemoveFromOldParent(*newChild.get(), targets, ec); - if (ec) - return false; + auto removeResult = collectChildrenAndRemoveFromOldParent(newChild, targets); + if (removeResult.hasException()) + return removeResult.releaseException(); if (targets.isEmpty()) - return true; + return { }; // We need this extra check because collectChildrenAndRemoveFromOldParent() can fire mutation events. - if (!checkAcceptChildGuaranteedNodeTypes(this, newChild.get(), ec)) - return false; + auto nodeTypeResult = checkAcceptChildGuaranteedNodeTypes(*this, newChild); + if (nodeTypeResult.hasException()) + return nodeTypeResult.releaseException(); - InspectorInstrumentation::willInsertDOMNode(&document(), this); + InspectorInstrumentation::willInsertDOMNode(document(), *this); // Now actually add the child(ren) ChildListMutationScope mutation(*this); - for (auto it = targets.begin(), end = targets.end(); it != end; ++it) { - Node& child = it->get(); - + for (auto& child : targets) { // If the child has a parent again, just stop what we're doing, because // that means someone is doing something with DOM mutation -- can't re-parent // a child that already has a parent. - if (child.parentNode()) + if (child->parentNode()) break; - treeScope().adoptIfNeeded(&child); - // Append child to the end of the list { NoEventDispatchAssertion assertNoEventDispatch; - appendChildToContainer(&child, *this); + treeScope().adoptIfNeeded(child); + appendChildCommon(child); } - updateTreeAfterInsertion(child); + updateTreeAfterInsertion(child.get()); } dispatchSubtreeModifiedEvent(); - return true; + return { }; } -void ContainerNode::parserAppendChild(PassRefPtr<Node> newChild) +void ContainerNode::parserAppendChild(Node& newChild) { - ASSERT(newChild); - ASSERT(!newChild->parentNode()); // Use appendChild if you need to handle reparenting (and want DOM mutation events). - ASSERT(!newChild->isDocumentFragment()); -#if ENABLE(TEMPLATE_ELEMENT) + ASSERT(!newChild.parentNode()); // Use appendChild if you need to handle reparenting (and want DOM mutation events). + ASSERT(!newChild.isDocumentFragment()); ASSERT(!hasTagName(HTMLNames::templateTag)); -#endif - - if (&document() != &newChild->document()) - document().adoptNode(newChild.get(), ASSERT_NO_EXCEPTION); { NoEventDispatchAssertion assertNoEventDispatch; - // FIXME: This method should take a PassRefPtr. - appendChildToContainer(newChild.get(), *this); - treeScope().adoptIfNeeded(newChild.get()); - } - - newChild->updateAncestorConnectedSubframeCountForInsertion(); - - ChildListMutationScope(*this).childAdded(*newChild); - - notifyChildInserted(*newChild, ChildChangeSourceParser); - - ChildNodeInsertionNotifier(*this).notify(*newChild); - newChild->setNeedsStyleRecalc(ReconstructRenderTree); -} - -void ContainerNode::suspendPostAttachCallbacks(Document& document) -{ - if (!s_attachDepth) { - ASSERT(!s_shouldReEnableMemoryCacheCallsAfterAttach); - if (Page* page = document.page()) { - // FIXME: How can this call be specific to one Page, while the - // s_attachDepth is a global? Doesn't make sense. - if (page->areMemoryCacheClientCallsEnabled()) { - page->setMemoryCacheClientCallsEnabled(false); - s_shouldReEnableMemoryCacheCallsAfterAttach = true; - } - } - platformStrategies()->loaderStrategy()->resourceLoadScheduler()->suspendPendingRequests(); - } - ++s_attachDepth; -} + if (&document() != &newChild.document()) + document().adoptNode(newChild); -void ContainerNode::resumePostAttachCallbacks(Document& document) -{ - if (s_attachDepth == 1) { - Ref<Document> protect(document); - - if (s_postAttachCallbackQueue) - dispatchPostAttachCallbacks(); - if (s_shouldReEnableMemoryCacheCallsAfterAttach) { - s_shouldReEnableMemoryCacheCallsAfterAttach = false; - if (Page* page = document.page()) - page->setMemoryCacheClientCallsEnabled(true); - } - platformStrategies()->loaderStrategy()->resourceLoadScheduler()->resumePendingRequests(); + appendChildCommon(newChild); + treeScope().adoptIfNeeded(newChild); } - --s_attachDepth; -} -void ContainerNode::queuePostAttachCallback(NodeCallback callback, Node& node, unsigned callbackData) -{ - if (!s_postAttachCallbackQueue) - s_postAttachCallbackQueue = new NodeCallbackQueue; - - s_postAttachCallbackQueue->append(CallbackInfo(callback, CallbackParameters(&node, callbackData))); -} + newChild.updateAncestorConnectedSubframeCountForInsertion(); -bool ContainerNode::postAttachCallbacksAreSuspended() -{ - return s_attachDepth; -} - -void ContainerNode::dispatchPostAttachCallbacks() -{ - // We recalculate size() each time through the loop because a callback - // can add more callbacks to the end of the queue. - for (size_t i = 0; i < s_postAttachCallbackQueue->size(); ++i) { - const CallbackInfo& info = (*s_postAttachCallbackQueue)[i]; - NodeCallback callback = info.first; - CallbackParameters params = info.second; - - callback(*params.first, params.second); - } - s_postAttachCallbackQueue->clear(); + notifyChildInserted(newChild, changeForChildInsertion(newChild, ChildChangeSourceParser)); } void ContainerNode::childrenChanged(const ChildChange& change) @@ -827,182 +796,30 @@ void ContainerNode::childrenChanged(const ChildChange& change) invalidateNodeListAndCollectionCachesInAncestors(); } -inline static void cloneChildNodesAvoidingDeleteButton(ContainerNode* parent, ContainerNode* clonedParent, HTMLElement* deleteButtonContainerElement) -{ - ExceptionCode ec = 0; - for (Node* child = parent->firstChild(); child && !ec; child = child->nextSibling()) { - -#if ENABLE(DELETION_UI) - if (child == deleteButtonContainerElement) - continue; -#else - UNUSED_PARAM(deleteButtonContainerElement); -#endif - - RefPtr<Node> clonedChild = child->cloneNode(false); - clonedParent->appendChild(clonedChild, ec); - - if (!ec && child->isContainerNode()) - cloneChildNodesAvoidingDeleteButton(toContainerNode(child), toContainerNode(clonedChild.get()), deleteButtonContainerElement); - } -} - -void ContainerNode::cloneChildNodes(ContainerNode *clone) -{ -#if ENABLE(DELETION_UI) - HTMLElement* deleteButtonContainerElement = 0; - if (Frame* frame = document().frame()) - deleteButtonContainerElement = frame->editor().deleteButtonController().containerElement(); - cloneChildNodesAvoidingDeleteButton(this, clone, deleteButtonContainerElement); -#else - cloneChildNodesAvoidingDeleteButton(this, clone, 0); -#endif -} - -bool ContainerNode::getUpperLeftCorner(FloatPoint& point) const -{ - if (!renderer()) - return false; - // What is this code really trying to do? - RenderObject* o = renderer(); - RenderObject* p = o; - - if (!o->isInline() || o->isReplaced()) { - point = o->localToAbsolute(FloatPoint(), UseTransforms); - return true; - } - - // find the next text/image child, to get a position - while (o) { - p = o; - if (RenderObject* child = o->firstChildSlow()) - o = child; - else if (o->nextSibling()) - o = o->nextSibling(); - else { - RenderObject* next = 0; - while (!next && o->parent()) { - o = o->parent(); - next = o->nextSibling(); - } - o = next; - - if (!o) - break; - } - ASSERT(o); - - if (!o->isInline() || o->isReplaced()) { - point = o->localToAbsolute(FloatPoint(), UseTransforms); - return true; - } - - if (p->node() && p->node() == this && o->isText() && !toRenderText(o)->firstTextBox()) { - // do nothing - skip unrendered whitespace that is a child or next sibling of the anchor - } else if (o->isText() || o->isReplaced()) { - point = FloatPoint(); - if (o->isText() && toRenderText(o)->firstTextBox()) { - point.move(toRenderText(o)->linesBoundingBox().x(), toRenderText(o)->firstTextBox()->root().lineTop()); - } else if (o->isBox()) { - RenderBox* box = toRenderBox(o); - point.moveBy(box->location()); - } - point = o->container()->localToAbsolute(point, UseTransforms); - return true; - } - } - - // If the target doesn't have any children or siblings that could be used to calculate the scroll position, we must be - // at the end of the document. Scroll to the bottom. FIXME: who said anything about scrolling? - if (!o && document().view()) { - point = FloatPoint(0, document().view()->contentsHeight()); - return true; - } - return false; -} - -bool ContainerNode::getLowerRightCorner(FloatPoint& point) const +void ContainerNode::cloneChildNodes(ContainerNode& clone) { - if (!renderer()) - return false; - - RenderObject* o = renderer(); - if (!o->isInline() || o->isReplaced()) { - RenderBox* box = toRenderBox(o); - point = o->localToAbsolute(LayoutPoint(box->size()), UseTransforms); - return true; + Document& targetDocument = clone.document(); + for (Node* child = firstChild(); child; child = child->nextSibling()) { + auto clonedChild = child->cloneNodeInternal(targetDocument, CloningOperation::SelfWithTemplateContent); + if (!clone.appendChild(clonedChild).hasException() && is<ContainerNode>(*child)) + downcast<ContainerNode>(*child).cloneChildNodes(downcast<ContainerNode>(clonedChild.get())); } - - // find the last text/image child, to get a position - while (o) { - if (RenderObject* child = o->lastChildSlow()) - o = child; - else if (o->previousSibling()) - o = o->previousSibling(); - else { - RenderObject* prev = 0; - while (!prev) { - o = o->parent(); - if (!o) - return false; - prev = o->previousSibling(); - } - o = prev; - } - ASSERT(o); - if (o->isText() || o->isReplaced()) { - point = FloatPoint(); - if (o->isText()) { - RenderText* text = toRenderText(o); - IntRect linesBox = text->linesBoundingBox(); - if (!linesBox.maxX() && !linesBox.maxY()) - continue; - point.moveBy(linesBox.maxXMaxYCorner()); - } else { - RenderBox* box = toRenderBox(o); - point.moveBy(box->frameRect().maxXMaxYCorner()); - } - point = o->container()->localToAbsolute(point, UseTransforms); - return true; - } - } - return true; -} - -LayoutRect ContainerNode::boundingBox() const -{ - FloatPoint upperLeft, lowerRight; - bool foundUpperLeft = getUpperLeftCorner(upperLeft); - bool foundLowerRight = getLowerRightCorner(lowerRight); - - // If we've found one corner, but not the other, - // then we should just return a point at the corner that we did find. - if (foundUpperLeft != foundLowerRight) { - if (foundUpperLeft) - lowerRight = upperLeft; - else - upperLeft = lowerRight; - } - - return enclosingLayoutRect(FloatRect(upperLeft, lowerRight.expandedTo(upperLeft) - upperLeft)); } -unsigned ContainerNode::childNodeCount() const +unsigned ContainerNode::countChildNodes() const { unsigned count = 0; - Node *n; - for (n = firstChild(); n; n = n->nextSibling()) - count++; + for (Node* child = firstChild(); child; child = child->nextSibling()) + ++count; return count; } -Node *ContainerNode::childNode(unsigned index) const +Node* ContainerNode::traverseToChildAt(unsigned index) const { - unsigned i; - Node *n = firstChild(); - for (i = 0; n != 0 && i < index; i++) - n = n->nextSibling(); - return n; + Node* child = firstChild(); + for (; child && index > 0; --index) + child = child->nextSibling(); + return child; } static void dispatchChildInsertionEvents(Node& child) @@ -1010,7 +827,7 @@ static void dispatchChildInsertionEvents(Node& child) if (child.isInShadowTree()) return; - ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden()); + ASSERT_WITH_SECURITY_IMPLICATION(NoEventDispatchAssertion::isEventDispatchAllowedInSubtree(child)); RefPtr<Node> c = &child; Ref<Document> document(child.document()); @@ -1019,8 +836,8 @@ static void dispatchChildInsertionEvents(Node& child) c->dispatchScopedEvent(MutationEvent::create(eventNames().DOMNodeInsertedEvent, true, c->parentNode())); // dispatch the DOMNodeInsertedIntoDocument event to all descendants - if (c->inDocument() && document->hasListenerType(Document::DOMNODEINSERTEDINTODOCUMENT_LISTENER)) { - for (; c; c = NodeTraversal::next(c.get(), &child)) + if (c->isConnected() && document->hasListenerType(Document::DOMNODEINSERTEDINTODOCUMENT_LISTENER)) { + for (; c; c = NodeTraversal::next(*c, &child)) c->dispatchScopedEvent(MutationEvent::create(eventNames().DOMNodeInsertedIntoDocumentEvent, false)); } } @@ -1028,14 +845,14 @@ static void dispatchChildInsertionEvents(Node& child) static void dispatchChildRemovalEvents(Node& child) { if (child.isInShadowTree()) { - InspectorInstrumentation::willRemoveDOMNode(&child.document(), &child); + InspectorInstrumentation::willRemoveDOMNode(child.document(), child); return; } - ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden()); + ASSERT_WITH_SECURITY_IMPLICATION(NoEventDispatchAssertion::isEventDispatchAllowedInSubtree(child)); willCreatePossiblyOrphanedTreeByRemoval(&child); - InspectorInstrumentation::willRemoveDOMNode(&child.document(), &child); + InspectorInstrumentation::willRemoveDOMNode(child.document(), child); RefPtr<Node> c = &child; Ref<Document> document(child.document()); @@ -1045,93 +862,121 @@ static void dispatchChildRemovalEvents(Node& child) c->dispatchScopedEvent(MutationEvent::create(eventNames().DOMNodeRemovedEvent, true, c->parentNode())); // dispatch the DOMNodeRemovedFromDocument event to all descendants - if (c->inDocument() && document->hasListenerType(Document::DOMNODEREMOVEDFROMDOCUMENT_LISTENER)) { - for (; c; c = NodeTraversal::next(c.get(), &child)) + if (c->isConnected() && document->hasListenerType(Document::DOMNODEREMOVEDFROMDOCUMENT_LISTENER)) { + for (; c; c = NodeTraversal::next(*c, &child)) c->dispatchScopedEvent(MutationEvent::create(eventNames().DOMNodeRemovedFromDocumentEvent, false)); } } -void ContainerNode::updateTreeAfterInsertion(Node& child) +void ContainerNode::updateTreeAfterInsertion(Node& child, ReplacedAllChildren replacedAllChildren) { ASSERT(child.refCount()); - ChildListMutationScope(*this).childAdded(child); - - notifyChildInserted(child, ChildChangeSourceAPI); - - ChildNodeInsertionNotifier(*this).notify(child); - - child.setNeedsStyleRecalc(ReconstructRenderTree); + notifyChildInserted(child, changeForChildInsertion(child, ChildChangeSourceAPI, replacedAllChildren)); dispatchChildInsertionEvents(child); } -void ContainerNode::setAttributeEventListener(const AtomicString& eventType, const QualifiedName& attributeName, const AtomicString& attributeValue) +ExceptionOr<Element*> ContainerNode::querySelector(const String& selectors) { - setAttributeEventListener(eventType, JSLazyEventListener::createForNode(*this, attributeName, attributeValue)); + auto query = document().selectorQueryForString(selectors); + if (query.hasException()) + return query.releaseException(); + return query.releaseReturnValue().queryFirst(*this); } -Element* ContainerNode::querySelector(const AtomicString& selectors, ExceptionCode& ec) +ExceptionOr<Ref<NodeList>> ContainerNode::querySelectorAll(const String& selectors) { - if (selectors.isEmpty()) { - ec = SYNTAX_ERR; - return nullptr; - } + auto query = document().selectorQueryForString(selectors); + if (query.hasException()) + return query.releaseException(); + return query.releaseReturnValue().queryAll(*this); +} - SelectorQuery* selectorQuery = document().selectorQueryCache().add(selectors, document(), ec); - if (!selectorQuery) - return nullptr; - return selectorQuery->queryFirst(*this); +Ref<HTMLCollection> ContainerNode::getElementsByTagName(const AtomicString& qualifiedName) +{ + ASSERT(!qualifiedName.isNull()); + + if (qualifiedName == starAtom) + return ensureRareData().ensureNodeLists().addCachedCollection<AllDescendantsCollection>(*this, AllDescendants); + + if (document().isHTMLDocument()) + return ensureRareData().ensureNodeLists().addCachedCollection<HTMLTagCollection>(*this, ByHTMLTag, qualifiedName); + return ensureRareData().ensureNodeLists().addCachedCollection<TagCollection>(*this, ByTag, qualifiedName); } -RefPtr<NodeList> ContainerNode::querySelectorAll(const AtomicString& selectors, ExceptionCode& ec) +Ref<HTMLCollection> ContainerNode::getElementsByTagNameNS(const AtomicString& namespaceURI, const AtomicString& localName) { - if (selectors.isEmpty()) { - ec = SYNTAX_ERR; - return nullptr; - } + ASSERT(!localName.isNull()); + return ensureRareData().ensureNodeLists().addCachedTagCollectionNS(*this, namespaceURI.isEmpty() ? nullAtom : namespaceURI, localName); +} + +Ref<NodeList> ContainerNode::getElementsByName(const String& elementName) +{ + return ensureRareData().ensureNodeLists().addCacheWithAtomicName<NameNodeList>(*this, elementName); +} - SelectorQuery* selectorQuery = document().selectorQueryCache().add(selectors, document(), ec); - if (!selectorQuery) - return nullptr; - return selectorQuery->queryAll(*this); +Ref<HTMLCollection> ContainerNode::getElementsByClassName(const AtomicString& classNames) +{ + return ensureRareData().ensureNodeLists().addCachedCollection<ClassCollection>(*this, ByClass, classNames); } -PassRefPtr<NodeList> ContainerNode::getElementsByTagName(const AtomicString& localName) +Ref<RadioNodeList> ContainerNode::radioNodeList(const AtomicString& name) { - if (localName.isNull()) - return 0; + ASSERT(hasTagName(HTMLNames::formTag) || hasTagName(HTMLNames::fieldsetTag)); + return ensureRareData().ensureNodeLists().addCacheWithAtomicName<RadioNodeList>(*this, name); +} - if (document().isHTMLDocument()) - return ensureRareData().ensureNodeLists().addCacheWithAtomicName<HTMLTagNodeList>(*this, LiveNodeList::HTMLTagNodeListType, localName); - return ensureRareData().ensureNodeLists().addCacheWithAtomicName<TagNodeList>(*this, LiveNodeList::TagNodeListType, localName); +Ref<HTMLCollection> ContainerNode::children() +{ + return ensureRareData().ensureNodeLists().addCachedCollection<GenericCachedHTMLCollection<CollectionTypeTraits<NodeChildren>::traversalType>>(*this, NodeChildren); } -PassRefPtr<NodeList> ContainerNode::getElementsByTagNameNS(const AtomicString& namespaceURI, const AtomicString& localName) +Element* ContainerNode::firstElementChild() const { - if (localName.isNull()) - return 0; + return ElementTraversal::firstChild(*this); +} - if (namespaceURI == starAtom) - return getElementsByTagName(localName); +Element* ContainerNode::lastElementChild() const +{ + return ElementTraversal::lastChild(*this); +} - return ensureRareData().ensureNodeLists().addCacheWithQualifiedName(*this, namespaceURI.isEmpty() ? nullAtom : namespaceURI, localName); +unsigned ContainerNode::childElementCount() const +{ + auto children = childrenOfType<Element>(*this); + return std::distance(children.begin(), children.end()); } -PassRefPtr<NodeList> ContainerNode::getElementsByName(const String& elementName) +ExceptionOr<void> ContainerNode::append(Vector<NodeOrString>&& vector) { - return ensureRareData().ensureNodeLists().addCacheWithAtomicName<NameNodeList>(*this, LiveNodeList::NameNodeListType, elementName); + auto result = convertNodesOrStringsIntoNode(WTFMove(vector)); + if (result.hasException()) + return result.releaseException(); + + auto node = result.releaseReturnValue(); + if (!node) + return { }; + + return appendChild(*node); } -PassRefPtr<NodeList> ContainerNode::getElementsByClassName(const String& classNames) +ExceptionOr<void> ContainerNode::prepend(Vector<NodeOrString>&& vector) { - return ensureRareData().ensureNodeLists().addCacheWithName<ClassNodeList>(*this, LiveNodeList::ClassNodeListType, classNames); + auto result = convertNodesOrStringsIntoNode(WTFMove(vector)); + if (result.hasException()) + return result.releaseException(); + + auto node = result.releaseReturnValue(); + if (!node) + return { }; + + return insertBefore(*node, firstChild()); } -PassRefPtr<RadioNodeList> ContainerNode::radioNodeList(const AtomicString& name) +HTMLCollection* ContainerNode::cachedHTMLCollection(CollectionType type) { - ASSERT(hasTagName(HTMLNames::formTag) || hasTagName(HTMLNames::fieldsetTag)); - return ensureRareData().ensureNodeLists().addCacheWithAtomicName<RadioNodeList>(*this, LiveNodeList::RadioNodeListType, name); + return hasRareData() && rareData()->nodeLists() ? rareData()->nodeLists()->cachedCollection<HTMLCollection>(type) : nullptr; } } // namespace WebCore diff --git a/Source/WebCore/dom/ContainerNode.h b/Source/WebCore/dom/ContainerNode.h index 163753026..8a8e0a2d7 100644 --- a/Source/WebCore/dom/ContainerNode.h +++ b/Source/WebCore/dom/ContainerNode.h @@ -2,7 +2,7 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2004-2015 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -21,222 +21,177 @@ * */ -#ifndef ContainerNode_h -#define ContainerNode_h +#pragma once -#include "ExceptionCodePlaceholder.h" +#include "CollectionType.h" #include "Node.h" -#include <wtf/OwnPtr.h> -#include <wtf/Vector.h> - namespace WebCore { -class FloatPoint; +class HTMLCollection; +class RadioNodeList; class RenderElement; -typedef void (*NodeCallback)(Node&, unsigned); - -namespace Private { - template<class GenericNode, class GenericNodeContainer> - void addChildNodesToDeletionQueue(GenericNode*& head, GenericNode*& tail, GenericNodeContainer&); -}; - -class NoEventDispatchAssertion { -public: - NoEventDispatchAssertion() - { -#ifndef NDEBUG - if (!isMainThread()) - return; - s_count++; -#endif - } - - ~NoEventDispatchAssertion() - { -#ifndef NDEBUG - if (!isMainThread()) - return; - ASSERT(s_count); - s_count--; -#endif - } - -#ifndef NDEBUG - static bool isEventDispatchForbidden() - { - if (!isMainThread()) - return false; - return s_count; - } -#endif - -private: -#ifndef NDEBUG - static unsigned s_count; -#endif -}; - class ContainerNode : public Node { - friend class PostAttachCallbackDisabler; public: virtual ~ContainerNode(); Node* firstChild() const { return m_firstChild; } + static ptrdiff_t firstChildMemoryOffset() { return OBJECT_OFFSETOF(ContainerNode, m_firstChild); } Node* lastChild() const { return m_lastChild; } + static ptrdiff_t lastChildMemoryOffset() { return OBJECT_OFFSETOF(ContainerNode, m_lastChild); } bool hasChildNodes() const { return m_firstChild; } + bool hasOneChild() const { return m_firstChild && !m_firstChild->nextSibling(); } + + bool directChildNeedsStyleRecalc() const { return getFlag(DirectChildNeedsStyleRecalcFlag); } + void setDirectChildNeedsStyleRecalc() { setFlag(DirectChildNeedsStyleRecalcFlag); } - unsigned childNodeCount() const; - Node* childNode(unsigned index) const; + WEBCORE_EXPORT unsigned countChildNodes() const; + WEBCORE_EXPORT Node* traverseToChildAt(unsigned) const; - bool insertBefore(PassRefPtr<Node> newChild, Node* refChild, ExceptionCode& = ASSERT_NO_EXCEPTION); - bool replaceChild(PassRefPtr<Node> newChild, Node* oldChild, ExceptionCode& = ASSERT_NO_EXCEPTION); - bool removeChild(Node* child, ExceptionCode& = ASSERT_NO_EXCEPTION); - bool appendChild(PassRefPtr<Node> newChild, ExceptionCode& = ASSERT_NO_EXCEPTION); + ExceptionOr<void> insertBefore(Node& newChild, Node* refChild); + ExceptionOr<void> replaceChild(Node& newChild, Node& oldChild); + WEBCORE_EXPORT ExceptionOr<void> removeChild(Node& child); + WEBCORE_EXPORT ExceptionOr<void> appendChild(Node& newChild); + void replaceAllChildren(Ref<Node>&&); + void replaceAllChildren(std::nullptr_t); // These methods are only used during parsing. // They don't send DOM mutation events or handle reparenting. // However, arbitrary code may be run by beforeload handlers. - void parserAppendChild(PassRefPtr<Node>); + void parserAppendChild(Node&); void parserRemoveChild(Node&); - void parserInsertBefore(PassRefPtr<Node> newChild, Node* refChild); + void parserInsertBefore(Node& newChild, Node& refChild); void removeChildren(); - void takeAllChildrenFrom(ContainerNode*); - void cloneChildNodes(ContainerNode* clone); + void takeAllChildrenFrom(ContainerNode*); - virtual LayoutRect boundingBox() const override; + void cloneChildNodes(ContainerNode& clone); - enum ChildChangeType { ElementInserted, ElementRemoved, TextInserted, TextRemoved, TextChanged, AllChildrenRemoved, NonContentsChildChanged }; + enum ChildChangeType { ElementInserted, ElementRemoved, TextInserted, TextRemoved, TextChanged, AllChildrenRemoved, NonContentsChildRemoved, NonContentsChildInserted, AllChildrenReplaced }; enum ChildChangeSource { ChildChangeSourceParser, ChildChangeSourceAPI }; struct ChildChange { ChildChangeType type; Element* previousSiblingElement; Element* nextSiblingElement; ChildChangeSource source; + + bool isInsertion() const + { + switch (type) { + case ElementInserted: + case TextInserted: + case NonContentsChildInserted: + case AllChildrenReplaced: + return true; + case ElementRemoved: + case TextRemoved: + case TextChanged: + case AllChildrenRemoved: + case NonContentsChildRemoved: + return false; + } + ASSERT_NOT_REACHED(); + return false; + } }; virtual void childrenChanged(const ChildChange&); void disconnectDescendantFrames(); - virtual bool childShouldCreateRenderer(const Node&) const { return true; } - - using Node::setAttributeEventListener; - void setAttributeEventListener(const AtomicString& eventType, const QualifiedName& attributeName, const AtomicString& value); - RenderElement* renderer() const; - Element* querySelector(const AtomicString& selectors, ExceptionCode&); - RefPtr<NodeList> querySelectorAll(const AtomicString& selectors, ExceptionCode&); + // Return a bounding box in absolute coordinates enclosing this node and all its descendants. + // This gives the area within which events may get handled by a hander registered on this node. + virtual LayoutRect absoluteEventHandlerBounds(bool& /* includesFixedPositionElements */) { return LayoutRect(); } - PassRefPtr<NodeList> getElementsByTagName(const AtomicString&); - PassRefPtr<NodeList> getElementsByTagNameNS(const AtomicString& namespaceURI, const AtomicString& localName); - PassRefPtr<NodeList> getElementsByName(const String& elementName); - PassRefPtr<NodeList> getElementsByClassName(const String& classNames); - PassRefPtr<RadioNodeList> radioNodeList(const AtomicString&); + WEBCORE_EXPORT ExceptionOr<Element*> querySelector(const String& selectors); + WEBCORE_EXPORT ExceptionOr<Ref<NodeList>> querySelectorAll(const String& selectors); -protected: - explicit ContainerNode(Document*, ConstructionType = CreateContainer); + WEBCORE_EXPORT Ref<HTMLCollection> getElementsByTagName(const AtomicString&); + WEBCORE_EXPORT Ref<HTMLCollection> getElementsByTagNameNS(const AtomicString& namespaceURI, const AtomicString& localName); + WEBCORE_EXPORT Ref<NodeList> getElementsByName(const String& elementName); + WEBCORE_EXPORT Ref<HTMLCollection> getElementsByClassName(const AtomicString& classNames); + Ref<RadioNodeList> radioNodeList(const AtomicString&); - static void queuePostAttachCallback(NodeCallback, Node&, unsigned = 0); - static bool postAttachCallbacksAreSuspended(); + // From the ParentNode interface - https://dom.spec.whatwg.org/#interface-parentnode + WEBCORE_EXPORT Ref<HTMLCollection> children(); + WEBCORE_EXPORT Element* firstElementChild() const; + WEBCORE_EXPORT Element* lastElementChild() const; + WEBCORE_EXPORT unsigned childElementCount() const; + ExceptionOr<void> append(Vector<NodeOrString>&&); + ExceptionOr<void> prepend(Vector<NodeOrString>&&); - template<class GenericNode, class GenericNodeContainer> - friend void appendChildToContainer(GenericNode* child, GenericNodeContainer&); + ExceptionOr<void> ensurePreInsertionValidity(Node& newChild, Node* refChild); - template<class GenericNode, class GenericNodeContainer> - friend void Private::addChildNodesToDeletionQueue(GenericNode*& head, GenericNode*& tail, GenericNodeContainer&); +protected: + explicit ContainerNode(Document&, ConstructionType = CreateContainer); + + friend void addChildNodesToDeletionQueue(Node*& head, Node*& tail, ContainerNode&); void removeDetachedChildren(); void setFirstChild(Node* child) { m_firstChild = child; } void setLastChild(Node* child) { m_lastChild = child; } + HTMLCollection* cachedHTMLCollection(CollectionType); + private: void removeBetween(Node* previousChild, Node* nextChild, Node& oldChild); + ExceptionOr<void> appendChildWithoutPreInsertionValidityCheck(Node&); void insertBeforeCommon(Node& nextChild, Node& oldChild); + void appendChildCommon(Node&); - static void dispatchPostAttachCallbacks(); - static void suspendPostAttachCallbacks(Document&); - static void resumePostAttachCallbacks(Document&); - - bool getUpperLeftCorner(FloatPoint&) const; - bool getLowerRightCorner(FloatPoint&) const; - - void notifyChildInserted(Node& child, ChildChangeSource); + void notifyChildInserted(Node& child, const ChildChange&); void notifyChildRemoved(Node& child, Node* previousSibling, Node* nextSibling, ChildChangeSource); - void updateTreeAfterInsertion(Node& child); + enum class ReplacedAllChildren { No, Yes }; + void updateTreeAfterInsertion(Node& child, ReplacedAllChildren = ReplacedAllChildren::No); + static ChildChange changeForChildInsertion(Node& child, ChildChangeSource, ReplacedAllChildren = ReplacedAllChildren::No); + void rebuildSVGExtensionsElementsIfNecessary(); bool isContainerNode() const = delete; - void willRemoveChild(Node& child); - - Node* m_firstChild; - Node* m_lastChild; + Node* m_firstChild { nullptr }; + Node* m_lastChild { nullptr }; }; -inline bool isContainerNode(const Node& node) { return node.isContainerNode(); } -void isContainerNode(const ContainerNode&); // Catch unnecessary runtime check of type known at compile time. - -NODE_TYPE_CASTS(ContainerNode) - -inline ContainerNode::ContainerNode(Document* document, ConstructionType type) +inline ContainerNode::ContainerNode(Document& document, ConstructionType type) : Node(document, type) - , m_firstChild(0) - , m_lastChild(0) { } -inline unsigned Node::childNodeCount() const +inline unsigned Node::countChildNodes() const { - if (!isContainerNode()) + if (!is<ContainerNode>(*this)) return 0; - return toContainerNode(this)->childNodeCount(); + return downcast<ContainerNode>(*this).countChildNodes(); } -inline Node* Node::childNode(unsigned index) const +inline Node* Node::traverseToChildAt(unsigned index) const { - if (!isContainerNode()) - return 0; - return toContainerNode(this)->childNode(index); + if (!is<ContainerNode>(*this)) + return nullptr; + return downcast<ContainerNode>(*this).traverseToChildAt(index); } inline Node* Node::firstChild() const { - if (!isContainerNode()) - return 0; - return toContainerNode(this)->firstChild(); + if (!is<ContainerNode>(*this)) + return nullptr; + return downcast<ContainerNode>(*this).firstChild(); } inline Node* Node::lastChild() const { - if (!isContainerNode()) - return 0; - return toContainerNode(this)->lastChild(); -} - -inline Node* Node::highestAncestor() const -{ - Node* node = const_cast<Node*>(this); - Node* highest = node; - for (; node; node = node->parentNode()) - highest = node; - return highest; -} - -inline bool Node::needsNodeRenderingTraversalSlowPath() const -{ - if (getFlag(NeedsNodeRenderingTraversalSlowPathFlag)) - return true; - ContainerNode* parent = parentOrShadowHostNode(); - return parent && parent->getFlag(NeedsNodeRenderingTraversalSlowPathFlag); + if (!is<ContainerNode>(*this)) + return nullptr; + return downcast<ContainerNode>(*this).lastChild(); } inline bool Node::isTreeScope() const { - return treeScope().rootNode() == this; + return &treeScope().rootNode() == this; } // This constant controls how much buffer is initially allocated @@ -259,45 +214,45 @@ public: explicit ChildNodesLazySnapshot(Node& parentNode) : m_currentNode(parentNode.firstChild()) , m_currentIndex(0) + , m_hasSnapshot(false) { m_nextSnapshot = latestSnapshot; latestSnapshot = this; } - ~ChildNodesLazySnapshot() + ALWAYS_INLINE ~ChildNodesLazySnapshot() { latestSnapshot = m_nextSnapshot; } // Returns 0 if there is no next Node. - PassRefPtr<Node> nextNode() + RefPtr<Node> nextNode() { if (LIKELY(!hasSnapshot())) { - RefPtr<Node> node = m_currentNode; + RefPtr<Node> node = WTFMove(m_currentNode); if (node) m_currentNode = node->nextSibling(); - return node.release(); + return node; } - Vector<RefPtr<Node>>& nodeVector = *m_childNodes; - if (m_currentIndex >= nodeVector.size()) - return 0; - return nodeVector[m_currentIndex++]; + if (m_currentIndex >= m_snapshot.size()) + return nullptr; + return m_snapshot[m_currentIndex++]; } void takeSnapshot() { if (hasSnapshot()) return; - m_childNodes = adoptPtr(new Vector<RefPtr<Node>>()); + m_hasSnapshot = true; Node* node = m_currentNode.get(); while (node) { - m_childNodes->append(node); + m_snapshot.append(node); node = node->nextSibling(); } } ChildNodesLazySnapshot* nextSnapshot() { return m_nextSnapshot; } - bool hasSnapshot() { return !!m_childNodes.get(); } + bool hasSnapshot() { return m_hasSnapshot; } static void takeChildNodesLazySnapshot() { @@ -313,10 +268,13 @@ private: RefPtr<Node> m_currentNode; unsigned m_currentIndex; - OwnPtr<Vector<RefPtr<Node>>> m_childNodes; // Lazily instantiated. + bool m_hasSnapshot; + Vector<RefPtr<Node>> m_snapshot; // Lazily instantiated. ChildNodesLazySnapshot* m_nextSnapshot; }; } // namespace WebCore -#endif // ContainerNode_h +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ContainerNode) + static bool isType(const WebCore::Node& node) { return node.isContainerNode(); } +SPECIALIZE_TYPE_TRAITS_END() diff --git a/Source/WebCore/dom/ContainerNodeAlgorithms.cpp b/Source/WebCore/dom/ContainerNodeAlgorithms.cpp index 9750df147..2958ec28f 100644 --- a/Source/WebCore/dom/ContainerNodeAlgorithms.cpp +++ b/Source/WebCore/dom/ContainerNodeAlgorithms.cpp @@ -2,7 +2,7 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2015 Apple Inc. All rights reserved. * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * Copyright (C) 2012 Google Inc. All rights reserved. @@ -26,75 +26,204 @@ #include "config.h" #include "ContainerNodeAlgorithms.h" +#include "HTMLFrameOwnerElement.h" +#include "HTMLTextAreaElement.h" +#include "InspectorInstrumentation.h" +#include "NoEventDispatchAssertion.h" +#include "ShadowRoot.h" namespace WebCore { -void ChildNodeInsertionNotifier::notifyDescendantInsertedIntoDocument(ContainerNode& node) +static void notifyNodeInsertedIntoTree(ContainerNode& insertionPoint, ContainerNode&, NodeVector& postInsertionNotificationTargets); +static void notifyNodeInsertedIntoDocument(ContainerNode& insertionPoint, Node&, NodeVector& postInsertionNotificationTargets); +static void notifyNodeRemovedFromTree(ContainerNode& insertionPoint, ContainerNode&); +static void notifyNodeRemovedFromDocument(ContainerNode& insertionPoint, Node&); + +static void notifyDescendantInsertedIntoDocument(ContainerNode& insertionPoint, ContainerNode& node, NodeVector& postInsertionNotificationTargets) { ChildNodesLazySnapshot snapshot(node); while (RefPtr<Node> child = snapshot.nextNode()) { // If we have been removed from the document during this loop, then // we don't want to tell the rest of our children that they've been // inserted into the document because they haven't. - if (node.inDocument() && child->parentNode() == &node) - notifyNodeInsertedIntoDocument(*child.get()); + if (node.isConnected() && child->parentNode() == &node) + notifyNodeInsertedIntoDocument(insertionPoint, *child, postInsertionNotificationTargets); } - if (!node.isElementNode()) + if (!is<Element>(node)) return; - if (RefPtr<ShadowRoot> root = toElement(node).shadowRoot()) { - if (node.inDocument() && root->hostElement() == &node) - notifyNodeInsertedIntoDocument(*root.get()); + if (RefPtr<ShadowRoot> root = downcast<Element>(node).shadowRoot()) { + if (node.isConnected() && root->host() == &node) + notifyNodeInsertedIntoDocument(insertionPoint, *root, postInsertionNotificationTargets); } } -void ChildNodeInsertionNotifier::notifyDescendantInsertedIntoTree(ContainerNode& node) +static void notifyDescendantInsertedIntoTree(ContainerNode& insertionPoint, ContainerNode& node, NodeVector& postInsertionNotificationTargets) { for (Node* child = node.firstChild(); child; child = child->nextSibling()) { - if (child->isContainerNode()) - notifyNodeInsertedIntoTree(*toContainerNode(child)); + if (is<ContainerNode>(*child)) + notifyNodeInsertedIntoTree(insertionPoint, downcast<ContainerNode>(*child), postInsertionNotificationTargets); } if (ShadowRoot* root = node.shadowRoot()) - notifyNodeInsertedIntoTree(*root); + notifyNodeInsertedIntoTree(insertionPoint, *root, postInsertionNotificationTargets); +} + +void notifyNodeInsertedIntoDocument(ContainerNode& insertionPoint, Node& node, NodeVector& postInsertionNotificationTargets) +{ + ASSERT(insertionPoint.isConnected()); + if (node.insertedInto(insertionPoint) == Node::InsertionShouldCallFinishedInsertingSubtree) + postInsertionNotificationTargets.append(node); + if (is<ContainerNode>(node)) + notifyDescendantInsertedIntoDocument(insertionPoint, downcast<ContainerNode>(node), postInsertionNotificationTargets); } -void ChildNodeRemovalNotifier::notifyDescendantRemovedFromDocument(ContainerNode& node) +void notifyNodeInsertedIntoTree(ContainerNode& insertionPoint, ContainerNode& node, NodeVector& postInsertionNotificationTargets) { + NoEventDispatchAssertion assertNoEventDispatch; + ASSERT(!insertionPoint.isConnected()); + + if (node.insertedInto(insertionPoint) == Node::InsertionShouldCallFinishedInsertingSubtree) + postInsertionNotificationTargets.append(node); + notifyDescendantInsertedIntoTree(insertionPoint, node, postInsertionNotificationTargets); +} + +void notifyChildNodeInserted(ContainerNode& insertionPoint, Node& node, NodeVector& postInsertionNotificationTargets) +{ + ASSERT_WITH_SECURITY_IMPLICATION(NoEventDispatchAssertion::isEventDispatchAllowedInSubtree(insertionPoint)); + + InspectorInstrumentation::didInsertDOMNode(node.document(), node); + + Ref<Document> protectDocument(node.document()); + Ref<Node> protectNode(node); + + if (insertionPoint.isConnected()) + notifyNodeInsertedIntoDocument(insertionPoint, node, postInsertionNotificationTargets); + else if (is<ContainerNode>(node)) + notifyNodeInsertedIntoTree(insertionPoint, downcast<ContainerNode>(node), postInsertionNotificationTargets); +} + +void notifyNodeRemovedFromDocument(ContainerNode& insertionPoint, Node& node) +{ + ASSERT(insertionPoint.isConnected()); + node.removedFrom(insertionPoint); + + if (!is<ContainerNode>(node)) + return; ChildNodesLazySnapshot snapshot(node); while (RefPtr<Node> child = snapshot.nextNode()) { // If we have been added to the document during this loop, then we // don't want to tell the rest of our children that they've been // removed from the document because they haven't. - if (!node.inDocument() && child->parentNode() == &node) - notifyNodeRemovedFromDocument(*child.get()); + if (!node.isConnected() && child->parentNode() == &node) + notifyNodeRemovedFromDocument(insertionPoint, *child.get()); } - if (!node.isElementNode()) + if (!is<Element>(node)) return; if (node.document().cssTarget() == &node) - node.document().setCSSTarget(0); + node.document().setCSSTarget(nullptr); - if (RefPtr<ShadowRoot> root = toElement(node).shadowRoot()) { - if (!node.inDocument() && root->hostElement() == &node) - notifyNodeRemovedFromDocument(*root.get()); + if (RefPtr<ShadowRoot> root = downcast<Element>(node).shadowRoot()) { + if (!node.isConnected() && root->host() == &node) + notifyNodeRemovedFromDocument(insertionPoint, *root.get()); } } -void ChildNodeRemovalNotifier::notifyDescendantRemovedFromTree(ContainerNode& node) +void notifyNodeRemovedFromTree(ContainerNode& insertionPoint, ContainerNode& node) { + NoEventDispatchAssertion assertNoEventDispatch; + ASSERT(!insertionPoint.isConnected()); + + node.removedFrom(insertionPoint); + for (Node* child = node.firstChild(); child; child = child->nextSibling()) { - if (child->isContainerNode()) - notifyNodeRemovedFromTree(*toContainerNode(child)); + if (is<ContainerNode>(*child)) + notifyNodeRemovedFromTree(insertionPoint, downcast<ContainerNode>(*child)); } - if (!node.isElementNode()) + if (!is<Element>(node)) return; - if (RefPtr<ShadowRoot> root = toElement(node).shadowRoot()) - notifyNodeRemovedFromTree(*root.get()); + if (RefPtr<ShadowRoot> root = downcast<Element>(node).shadowRoot()) + notifyNodeRemovedFromTree(insertionPoint, *root.get()); +} + +void notifyChildNodeRemoved(ContainerNode& insertionPoint, Node& child) +{ + if (!child.isConnected()) { + if (is<ContainerNode>(child)) + notifyNodeRemovedFromTree(insertionPoint, downcast<ContainerNode>(child)); + return; + } + notifyNodeRemovedFromDocument(insertionPoint, child); +} + +void addChildNodesToDeletionQueue(Node*& head, Node*& tail, ContainerNode& container) +{ + // We have to tell all children that their parent has died. + Node* next = nullptr; + for (auto* node = container.firstChild(); node; node = next) { + ASSERT(!node->m_deletionHasBegun); + + next = node->nextSibling(); + node->setNextSibling(nullptr); + node->setParentNode(nullptr); + container.setFirstChild(next); + if (next) + next->setPreviousSibling(nullptr); + + if (!node->refCount()) { +#ifndef NDEBUG + node->m_deletionHasBegun = true; +#endif + // Add the node to the list of nodes to be deleted. + // Reuse the nextSibling pointer for this purpose. + if (tail) + tail->setNextSibling(node); + else + head = node; + + tail = node; + } else { + Ref<Node> protect(*node); // removedFromDocument may remove remove all references to this node. + if (Document* containerDocument = container.ownerDocument()) + containerDocument->adoptIfNeeded(*node); + if (node->isInTreeScope()) + notifyChildNodeRemoved(container, *node); + } + } + + container.setLastChild(nullptr); +} + +void removeDetachedChildrenInContainer(ContainerNode& container) +{ + // List of nodes to be deleted. + Node* head = nullptr; + Node* tail = nullptr; + + addChildNodesToDeletionQueue(head, tail, container); + + Node* node; + Node* next; + while ((node = head)) { + ASSERT(node->m_deletionHasBegun); + + next = node->nextSibling(); + node->setNextSibling(nullptr); + + head = next; + if (!next) + tail = nullptr; + + if (is<ContainerNode>(*node)) + addChildNodesToDeletionQueue(head, tail, downcast<ContainerNode>(*node)); + + delete node; + } } #ifndef NDEBUG @@ -102,11 +231,11 @@ static unsigned assertConnectedSubrameCountIsConsistent(ContainerNode& node) { unsigned count = 0; - if (node.isElementNode()) { - if (node.isFrameOwnerElement() && toHTMLFrameOwnerElement(node).contentFrame()) - count++; + if (is<Element>(node)) { + if (is<HTMLFrameOwnerElement>(node) && downcast<HTMLFrameOwnerElement>(node).contentFrame()) + ++count; - if (ShadowRoot* root = toElement(node).shadowRoot()) + if (ShadowRoot* root = downcast<Element>(node).shadowRoot()) count += assertConnectedSubrameCountIsConsistent(*root); } @@ -138,8 +267,8 @@ static void collectFrameOwners(Vector<Ref<HTMLFrameOwnerElement>>& frameOwners, continue; } - if (element.isHTMLElement() && element.isFrameOwnerElement()) - frameOwners.append(toHTMLFrameOwnerElement(element)); + if (is<HTMLFrameOwnerElement>(element)) + frameOwners.append(downcast<HTMLFrameOwnerElement>(element)); if (ShadowRoot* shadowRoot = element.shadowRoot()) collectFrameOwners(frameOwners, *shadowRoot); @@ -157,22 +286,26 @@ void disconnectSubframes(ContainerNode& root, SubframeDisconnectPolicy policy) Vector<Ref<HTMLFrameOwnerElement>> frameOwners; if (policy == RootAndDescendants) { - if (root.isHTMLElement() && root.isFrameOwnerElement()) - frameOwners.append(toHTMLFrameOwnerElement(root)); + if (is<HTMLFrameOwnerElement>(root)) + frameOwners.append(downcast<HTMLFrameOwnerElement>(root)); } collectFrameOwners(frameOwners, root); + if (auto* shadowRoot = root.shadowRoot()) + collectFrameOwners(frameOwners, *shadowRoot); + // Must disable frame loading in the subtree so an unload handler cannot // insert more frames and create loaded frames in detached subtrees. - SubframeLoadingDisabler disabler(root); + SubframeLoadingDisabler disabler(&root); - for (unsigned i = 0; i < frameOwners.size(); ++i) { - auto& owner = frameOwners[i].get(); + bool isFirst = true; + for (auto& owner : frameOwners) { // Don't need to traverse up the tree for the first owner since no // script could have moved it. - if (!i || root.containsIncludingShadowDOM(&owner)) - owner.disconnectContentFrame(); + if (isFirst || root.containsIncludingShadowDOM(&owner.get())) + owner.get().disconnectContentFrame(); + isFirst = false; } } diff --git a/Source/WebCore/dom/ContainerNodeAlgorithms.h b/Source/WebCore/dom/ContainerNodeAlgorithms.h index cea04980a..611a0444b 100644 --- a/Source/WebCore/dom/ContainerNodeAlgorithms.h +++ b/Source/WebCore/dom/ContainerNodeAlgorithms.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2015 Apple Inc. All rights reserved. * (C) 2008 Nikolas Zimmermann <zimmermann@kde.org> * * This library is free software; you can redistribute it and/or @@ -19,248 +19,20 @@ * */ -#ifndef ContainerNodeAlgorithms_h -#define ContainerNodeAlgorithms_h +#pragma once #include "Document.h" #include "ElementIterator.h" #include "Frame.h" -#include "HTMLFrameOwnerElement.h" -#include "InspectorInstrumentation.h" #include "NodeTraversal.h" -#include "ShadowRoot.h" #include <wtf/Assertions.h> #include <wtf/Ref.h> namespace WebCore { -class ChildNodeInsertionNotifier { -public: - explicit ChildNodeInsertionNotifier(ContainerNode& insertionPoint) - : m_insertionPoint(insertionPoint) - { - } - - void notify(Node&); - -private: - void notifyDescendantInsertedIntoDocument(ContainerNode&); - void notifyDescendantInsertedIntoTree(ContainerNode&); - void notifyNodeInsertedIntoDocument(Node&); - void notifyNodeInsertedIntoTree(ContainerNode&); - - ContainerNode& m_insertionPoint; - Vector<Ref<Node>> m_postInsertionNotificationTargets; -}; - -class ChildNodeRemovalNotifier { -public: - explicit ChildNodeRemovalNotifier(ContainerNode& insertionPoint) - : m_insertionPoint(insertionPoint) - { - } - - void notify(Node&); - -private: - void notifyDescendantRemovedFromDocument(ContainerNode&); - void notifyDescendantRemovedFromTree(ContainerNode&); - void notifyNodeRemovedFromDocument(Node&); - void notifyNodeRemovedFromTree(ContainerNode&); - - ContainerNode& m_insertionPoint; -}; - -namespace Private { - - template<class GenericNode, class GenericNodeContainer> - void addChildNodesToDeletionQueue(GenericNode*& head, GenericNode*& tail, GenericNodeContainer&); - -} - -// Helper functions for TreeShared-derived classes, which have a 'Node' style interface -// This applies to 'ContainerNode' and 'SVGElementInstance' -template<class GenericNode, class GenericNodeContainer> -inline void removeDetachedChildrenInContainer(GenericNodeContainer& container) -{ - // List of nodes to be deleted. - GenericNode* head = 0; - GenericNode* tail = 0; - - Private::addChildNodesToDeletionQueue<GenericNode, GenericNodeContainer>(head, tail, container); - - GenericNode* n; - GenericNode* next; - while ((n = head) != 0) { - ASSERT(n->m_deletionHasBegun); - - next = n->nextSibling(); - n->setNextSibling(0); - - head = next; - if (next == 0) - tail = 0; - - if (n->hasChildNodes()) - Private::addChildNodesToDeletionQueue<GenericNode, GenericNodeContainer>(head, tail, *static_cast<GenericNodeContainer*>(n)); - - delete n; - } -} - -template<class GenericNode, class GenericNodeContainer> -inline void appendChildToContainer(GenericNode* child, GenericNodeContainer& container) -{ - child->setParentNode(&container); - - GenericNode* lastChild = container.lastChild(); - if (lastChild) { - child->setPreviousSibling(lastChild); - lastChild->setNextSibling(child); - } else - container.setFirstChild(child); - - container.setLastChild(child); -} - -// Helper methods for removeDetachedChildrenInContainer, hidden from WebCore namespace -namespace Private { - - template<class GenericNode, class GenericNodeContainer, bool dispatchRemovalNotification> - struct NodeRemovalDispatcher { - static void dispatch(GenericNode&, GenericNodeContainer&) - { - // no-op, by default - } - }; - - template<class GenericNode, class GenericNodeContainer> - struct NodeRemovalDispatcher<GenericNode, GenericNodeContainer, true> { - static void dispatch(GenericNode& node, GenericNodeContainer& container) - { - // Clean up any TreeScope to a removed tree. - if (Document* containerDocument = container.ownerDocument()) - containerDocument->adoptIfNeeded(&node); - if (node.inDocument()) - ChildNodeRemovalNotifier(container).notify(node); - } - }; - - template<class GenericNode> - struct ShouldDispatchRemovalNotification { - static const bool value = false; - }; - - template<> - struct ShouldDispatchRemovalNotification<Node> { - static const bool value = true; - }; - - template<class GenericNode, class GenericNodeContainer> - void addChildNodesToDeletionQueue(GenericNode*& head, GenericNode*& tail, GenericNodeContainer& container) - { - // We have to tell all children that their parent has died. - GenericNode* next = 0; - for (GenericNode* n = container.firstChild(); n != 0; n = next) { - ASSERT(!n->m_deletionHasBegun); - - next = n->nextSibling(); - n->setNextSibling(0); - n->setParentNode(0); - container.setFirstChild(next); - if (next) - next->setPreviousSibling(0); - - if (!n->refCount()) { -#ifndef NDEBUG - n->m_deletionHasBegun = true; -#endif - // Add the node to the list of nodes to be deleted. - // Reuse the nextSibling pointer for this purpose. - if (tail) - tail->setNextSibling(n); - else - head = n; - - tail = n; - } else { - Ref<GenericNode> protect(*n); // removedFromDocument may remove remove all references to this node. - NodeRemovalDispatcher<GenericNode, GenericNodeContainer, ShouldDispatchRemovalNotification<GenericNode>::value>::dispatch(*n, container); - } - } - - container.setLastChild(0); - } - -} // namespace Private - -inline void ChildNodeInsertionNotifier::notifyNodeInsertedIntoDocument(Node& node) -{ - ASSERT(m_insertionPoint.inDocument()); - Ref<Node> protect(node); - if (Node::InsertionShouldCallDidNotifySubtreeInsertions == node.insertedInto(m_insertionPoint)) - m_postInsertionNotificationTargets.append(node); - if (node.isContainerNode()) - notifyDescendantInsertedIntoDocument(toContainerNode(node)); -} - -inline void ChildNodeInsertionNotifier::notifyNodeInsertedIntoTree(ContainerNode& node) -{ - NoEventDispatchAssertion assertNoEventDispatch; - ASSERT(!m_insertionPoint.inDocument()); - - if (Node::InsertionShouldCallDidNotifySubtreeInsertions == node.insertedInto(m_insertionPoint)) - m_postInsertionNotificationTargets.append(node); - notifyDescendantInsertedIntoTree(node); -} - -inline void ChildNodeInsertionNotifier::notify(Node& node) -{ - ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden()); - -#if ENABLE(INSPECTOR) - InspectorInstrumentation::didInsertDOMNode(&node.document(), &node); -#endif - - Ref<Document> protectDocument(node.document()); - Ref<Node> protectNode(node); - - if (m_insertionPoint.inDocument()) - notifyNodeInsertedIntoDocument(node); - else if (node.isContainerNode()) - notifyNodeInsertedIntoTree(toContainerNode(node)); - - for (size_t i = 0; i < m_postInsertionNotificationTargets.size(); ++i) - m_postInsertionNotificationTargets[i]->didNotifySubtreeInsertions(&m_insertionPoint); -} - - -inline void ChildNodeRemovalNotifier::notifyNodeRemovedFromDocument(Node& node) -{ - ASSERT(m_insertionPoint.inDocument()); - node.removedFrom(m_insertionPoint); - - if (node.isContainerNode()) - notifyDescendantRemovedFromDocument(toContainerNode(node)); -} - -inline void ChildNodeRemovalNotifier::notifyNodeRemovedFromTree(ContainerNode& node) -{ - NoEventDispatchAssertion assertNoEventDispatch; - ASSERT(!m_insertionPoint.inDocument()); - - node.removedFrom(m_insertionPoint); - notifyDescendantRemovedFromTree(node); -} - -inline void ChildNodeRemovalNotifier::notify(Node& node) -{ - if (node.inDocument()) { - notifyNodeRemovedFromDocument(node); - node.document().notifyRemovePendingSheetIfNeeded(); - } else if (node.isContainerNode()) - notifyNodeRemovedFromTree(toContainerNode(node)); -} +void notifyChildNodeInserted(ContainerNode& insertionPoint, Node&, NodeVector& postInsertionNotificationTargets); +void notifyChildNodeRemoved(ContainerNode& insertionPoint, Node&); +void removeDetachedChildrenInContainer(ContainerNode&); enum SubframeDisconnectPolicy { RootAndDescendants, @@ -276,5 +48,3 @@ inline void disconnectSubframesIfNeeded(ContainerNode& root, SubframeDisconnectP } } // namespace WebCore - -#endif // ContainerNodeAlgorithms_h diff --git a/Source/WebCore/dom/ContextDestructionObserver.cpp b/Source/WebCore/dom/ContextDestructionObserver.cpp index a557dc7c0..a3339180e 100644 --- a/Source/WebCore/dom/ContextDestructionObserver.cpp +++ b/Source/WebCore/dom/ContextDestructionObserver.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -32,34 +32,34 @@ namespace WebCore { ContextDestructionObserver::ContextDestructionObserver(ScriptExecutionContext* scriptExecutionContext) - : m_scriptExecutionContext(0) + : m_scriptExecutionContext(nullptr) { observeContext(scriptExecutionContext); } ContextDestructionObserver::~ContextDestructionObserver() { - observeContext(0); + observeContext(nullptr); } void ContextDestructionObserver::observeContext(ScriptExecutionContext* scriptExecutionContext) { if (m_scriptExecutionContext) { ASSERT(m_scriptExecutionContext->isContextThread()); - m_scriptExecutionContext->willDestroyDestructionObserver(this); + m_scriptExecutionContext->willDestroyDestructionObserver(*this); } m_scriptExecutionContext = scriptExecutionContext; if (m_scriptExecutionContext) { ASSERT(m_scriptExecutionContext->isContextThread()); - m_scriptExecutionContext->didCreateDestructionObserver(this); + m_scriptExecutionContext->didCreateDestructionObserver(*this); } } void ContextDestructionObserver::contextDestroyed() { - m_scriptExecutionContext = 0; + m_scriptExecutionContext = nullptr; } } // namespace WebCore diff --git a/Source/WebCore/dom/ContextDestructionObserver.h b/Source/WebCore/dom/ContextDestructionObserver.h index ca956abc8..ebb8d3e78 100644 --- a/Source/WebCore/dom/ContextDestructionObserver.h +++ b/Source/WebCore/dom/ContextDestructionObserver.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,8 +24,9 @@ * */ -#ifndef ContextDestructionObserver_h -#define ContextDestructionObserver_h +#pragma once + +#include "PlatformExportMacros.h" namespace WebCore { @@ -33,18 +34,16 @@ class ScriptExecutionContext; class ContextDestructionObserver { public: - explicit ContextDestructionObserver(ScriptExecutionContext*); - virtual void contextDestroyed(); + WEBCORE_EXPORT explicit ContextDestructionObserver(ScriptExecutionContext*); + WEBCORE_EXPORT virtual void contextDestroyed(); ScriptExecutionContext* scriptExecutionContext() const { return m_scriptExecutionContext; } protected: - virtual ~ContextDestructionObserver(); + WEBCORE_EXPORT virtual ~ContextDestructionObserver(); void observeContext(ScriptExecutionContext*); ScriptExecutionContext* m_scriptExecutionContext; }; } // namespace WebCore - -#endif // ContextDestructionObserver_h diff --git a/Source/WebCore/dom/CrossThreadTask.h b/Source/WebCore/dom/CrossThreadTask.h deleted file mode 100644 index 6cc5f8421..000000000 --- a/Source/WebCore/dom/CrossThreadTask.h +++ /dev/null @@ -1,476 +0,0 @@ -/* - * Copyright (C) 2009-2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef CrossThreadTask_h -#define CrossThreadTask_h - -#include "CrossThreadCopier.h" -#include "ScriptExecutionContext.h" -#include <memory> -#include <wtf/PassOwnPtr.h> -#include <wtf/PassRefPtr.h> - -namespace WebCore { - -// Traits for the CrossThreadTask. -template<typename T> struct CrossThreadTaskTraits { - typedef const T& ParamType; -}; - -template<typename T> struct CrossThreadTaskTraits<T*> { - typedef T* ParamType; -}; - -template<typename T> struct CrossThreadTaskTraits<PassRefPtr<T>> { - typedef PassRefPtr<T> ParamType; -}; - -template<typename T> struct CrossThreadTaskTraits<PassOwnPtr<T>> { - typedef PassOwnPtr<T> ParamType; -}; - -template<typename P1, typename MP1> -class CrossThreadTask1 : public ScriptExecutionContext::Task { -public: - typedef void (*Method)(ScriptExecutionContext*, MP1); - typedef CrossThreadTask1<P1, MP1> CrossThreadTask; - typedef typename CrossThreadTaskTraits<P1>::ParamType Param1; - - static PassOwnPtr<CrossThreadTask> create(Method method, Param1 parameter1) - { - return adoptPtr(new CrossThreadTask(method, parameter1)); - } - -private: - CrossThreadTask1(Method method, Param1 parameter1) - : m_method(method) - , m_parameter1(parameter1) - { - } - - virtual void performTask(ScriptExecutionContext* context) - { - (*m_method)(context, m_parameter1); - } - -private: - Method m_method; - P1 m_parameter1; -}; - -template<typename P1, typename MP1, typename P2, typename MP2> -class CrossThreadTask2 : public ScriptExecutionContext::Task { -public: - typedef void (*Method)(ScriptExecutionContext*, MP1, MP2); - typedef CrossThreadTask2<P1, MP1, P2, MP2> CrossThreadTask; - typedef typename CrossThreadTaskTraits<P1>::ParamType Param1; - typedef typename CrossThreadTaskTraits<P2>::ParamType Param2; - - static PassOwnPtr<CrossThreadTask> create(Method method, Param1 parameter1, Param2 parameter2) - { - return adoptPtr(new CrossThreadTask(method, parameter1, parameter2)); - } - -private: - CrossThreadTask2(Method method, Param1 parameter1, Param2 parameter2) - : m_method(method) - , m_parameter1(parameter1) - , m_parameter2(parameter2) - { - } - - virtual void performTask(ScriptExecutionContext* context) - { - (*m_method)(context, m_parameter1, m_parameter2); - } - -private: - Method m_method; - P1 m_parameter1; - P2 m_parameter2; -}; - -template<typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3> -class CrossThreadTask3 : public ScriptExecutionContext::Task { -public: - typedef void (*Method)(ScriptExecutionContext*, MP1, MP2, MP3); - typedef CrossThreadTask3<P1, MP1, P2, MP2, P3, MP3> CrossThreadTask; - typedef typename CrossThreadTaskTraits<P1>::ParamType Param1; - typedef typename CrossThreadTaskTraits<P2>::ParamType Param2; - typedef typename CrossThreadTaskTraits<P3>::ParamType Param3; - - static PassOwnPtr<CrossThreadTask> create(Method method, Param1 parameter1, Param2 parameter2, Param3 parameter3) - { - return adoptPtr(new CrossThreadTask(method, parameter1, parameter2, parameter3)); - } - -private: - CrossThreadTask3(Method method, Param1 parameter1, Param2 parameter2, Param3 parameter3) - : m_method(method) - , m_parameter1(parameter1) - , m_parameter2(parameter2) - , m_parameter3(parameter3) - { - } - - virtual void performTask(ScriptExecutionContext* context) - { - (*m_method)(context, m_parameter1, m_parameter2, m_parameter3); - } - -private: - Method m_method; - P1 m_parameter1; - P2 m_parameter2; - P3 m_parameter3; -}; - -template<typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3, typename P4, typename MP4> -class CrossThreadTask4 : public ScriptExecutionContext::Task { -public: - typedef void (*Method)(ScriptExecutionContext*, MP1, MP2, MP3, MP4); - typedef CrossThreadTask4<P1, MP1, P2, MP2, P3, MP3, P4, MP4> CrossThreadTask; - typedef typename CrossThreadTaskTraits<P1>::ParamType Param1; - typedef typename CrossThreadTaskTraits<P2>::ParamType Param2; - typedef typename CrossThreadTaskTraits<P3>::ParamType Param3; - typedef typename CrossThreadTaskTraits<P4>::ParamType Param4; - - static PassOwnPtr<CrossThreadTask> create(Method method, Param1 parameter1, Param2 parameter2, Param3 parameter3, Param4 parameter4) - { - return adoptPtr(new CrossThreadTask(method, parameter1, parameter2, parameter3, parameter4)); - } - -private: - CrossThreadTask4(Method method, Param1 parameter1, Param2 parameter2, Param3 parameter3, Param4 parameter4) - : m_method(method) - , m_parameter1(parameter1) - , m_parameter2(parameter2) - , m_parameter3(parameter3) - , m_parameter4(parameter4) - { - } - - virtual void performTask(ScriptExecutionContext* context) - { - (*m_method)(context, m_parameter1, m_parameter2, m_parameter3, m_parameter4); - } - -private: - Method m_method; - P1 m_parameter1; - P2 m_parameter2; - P3 m_parameter3; - P4 m_parameter4; -}; - -template<typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3, typename P4, typename MP4, typename P5, typename MP5> -class CrossThreadTask5 : public ScriptExecutionContext::Task { -public: - typedef void (*Method)(ScriptExecutionContext*, MP1, MP2, MP3, MP4, MP5); - typedef CrossThreadTask5<P1, MP1, P2, MP2, P3, MP3, P4, MP4, P5, MP5> CrossThreadTask; - typedef typename CrossThreadTaskTraits<P1>::ParamType Param1; - typedef typename CrossThreadTaskTraits<P2>::ParamType Param2; - typedef typename CrossThreadTaskTraits<P3>::ParamType Param3; - typedef typename CrossThreadTaskTraits<P4>::ParamType Param4; - typedef typename CrossThreadTaskTraits<P5>::ParamType Param5; - - static PassOwnPtr<CrossThreadTask> create(Method method, Param1 parameter1, Param2 parameter2, Param3 parameter3, Param4 parameter4, Param5 parameter5) - { - return adoptPtr(new CrossThreadTask(method, parameter1, parameter2, parameter3, parameter4, parameter5)); - } - -private: - CrossThreadTask5(Method method, Param1 parameter1, Param2 parameter2, Param3 parameter3, Param4 parameter4, Param5 parameter5) - : m_method(method) - , m_parameter1(parameter1) - , m_parameter2(parameter2) - , m_parameter3(parameter3) - , m_parameter4(parameter4) - , m_parameter5(parameter5) - { - } - - virtual void performTask(ScriptExecutionContext* context) - { - (*m_method)(context, m_parameter1, m_parameter2, m_parameter3, m_parameter4, m_parameter5); - } - -private: - Method m_method; - P1 m_parameter1; - P2 m_parameter2; - P3 m_parameter3; - P4 m_parameter4; - P5 m_parameter5; -}; - -template<typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3, typename P4, typename MP4, typename P5, typename MP5, typename P6, typename MP6> -class CrossThreadTask6 : public ScriptExecutionContext::Task { -public: - typedef void (*Method)(ScriptExecutionContext*, MP1, MP2, MP3, MP4, MP5, MP6); - typedef CrossThreadTask6<P1, MP1, P2, MP2, P3, MP3, P4, MP4, P5, MP5, P6, MP6> CrossThreadTask; - typedef typename CrossThreadTaskTraits<P1>::ParamType Param1; - typedef typename CrossThreadTaskTraits<P2>::ParamType Param2; - typedef typename CrossThreadTaskTraits<P3>::ParamType Param3; - typedef typename CrossThreadTaskTraits<P4>::ParamType Param4; - typedef typename CrossThreadTaskTraits<P5>::ParamType Param5; - typedef typename CrossThreadTaskTraits<P6>::ParamType Param6; - - static PassOwnPtr<CrossThreadTask> create(Method method, Param1 parameter1, Param2 parameter2, Param3 parameter3, Param4 parameter4, Param5 parameter5, Param6 parameter6) - { - return adoptPtr(new CrossThreadTask(method, parameter1, parameter2, parameter3, parameter4, parameter5, parameter6)); - } - -private: - CrossThreadTask6(Method method, Param1 parameter1, Param2 parameter2, Param3 parameter3, Param4 parameter4, Param5 parameter5, Param6 parameter6) - : m_method(method) - , m_parameter1(parameter1) - , m_parameter2(parameter2) - , m_parameter3(parameter3) - , m_parameter4(parameter4) - , m_parameter5(parameter5) - , m_parameter6(parameter6) - { - } - - virtual void performTask(ScriptExecutionContext* context) - { - (*m_method)(context, m_parameter1, m_parameter2, m_parameter3, m_parameter4, m_parameter5, m_parameter6); - } - -private: - Method m_method; - P1 m_parameter1; - P2 m_parameter2; - P3 m_parameter3; - P4 m_parameter4; - P5 m_parameter5; - P6 m_parameter6; -}; - -template<typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3, typename P4, typename MP4, typename P5, typename MP5, typename P6, typename MP6, typename P7, typename MP7> -class CrossThreadTask7 : public ScriptExecutionContext::Task { -public: - typedef void (*Method)(ScriptExecutionContext*, MP1, MP2, MP3, MP4, MP5, MP6, MP7); - typedef CrossThreadTask7<P1, MP1, P2, MP2, P3, MP3, P4, MP4, P5, MP5, P6, MP6, P7, MP7> CrossThreadTask; - typedef typename CrossThreadTaskTraits<P1>::ParamType Param1; - typedef typename CrossThreadTaskTraits<P2>::ParamType Param2; - typedef typename CrossThreadTaskTraits<P3>::ParamType Param3; - typedef typename CrossThreadTaskTraits<P4>::ParamType Param4; - typedef typename CrossThreadTaskTraits<P5>::ParamType Param5; - typedef typename CrossThreadTaskTraits<P6>::ParamType Param6; - typedef typename CrossThreadTaskTraits<P7>::ParamType Param7; - - static PassOwnPtr<CrossThreadTask> create(Method method, Param1 parameter1, Param2 parameter2, Param3 parameter3, Param4 parameter4, Param5 parameter5, Param6 parameter6, Param7 parameter7) - { - return adoptPtr(new CrossThreadTask(method, parameter1, parameter2, parameter3, parameter4, parameter5, parameter6, parameter7)); - } - -private: - CrossThreadTask7(Method method, Param1 parameter1, Param2 parameter2, Param3 parameter3, Param4 parameter4, Param5 parameter5, Param6 parameter6, Param7 parameter7) - : m_method(method) - , m_parameter1(parameter1) - , m_parameter2(parameter2) - , m_parameter3(parameter3) - , m_parameter4(parameter4) - , m_parameter5(parameter5) - , m_parameter6(parameter6) - , m_parameter7(parameter7) - { - } - - virtual void performTask(ScriptExecutionContext* context) - { - (*m_method)(context, m_parameter1, m_parameter2, m_parameter3, m_parameter4, m_parameter5, m_parameter6, m_parameter7); - } - -private: - Method m_method; - P1 m_parameter1; - P2 m_parameter2; - P3 m_parameter3; - P4 m_parameter4; - P5 m_parameter5; - P6 m_parameter6; - P7 m_parameter7; -}; - -template<typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3, typename P4, typename MP4, typename P5, typename MP5, typename P6, typename MP6, typename P7, typename MP7, typename P8, typename MP8> -class CrossThreadTask8 : public ScriptExecutionContext::Task { -public: - typedef void (*Method)(ScriptExecutionContext*, MP1, MP2, MP3, MP4, MP5, MP6, MP7, MP8); - typedef CrossThreadTask8<P1, MP1, P2, MP2, P3, MP3, P4, MP4, P5, MP5, P6, MP6, P7, MP7, P8, MP8> CrossThreadTask; - typedef typename CrossThreadTaskTraits<P1>::ParamType Param1; - typedef typename CrossThreadTaskTraits<P2>::ParamType Param2; - typedef typename CrossThreadTaskTraits<P3>::ParamType Param3; - typedef typename CrossThreadTaskTraits<P4>::ParamType Param4; - typedef typename CrossThreadTaskTraits<P5>::ParamType Param5; - typedef typename CrossThreadTaskTraits<P6>::ParamType Param6; - typedef typename CrossThreadTaskTraits<P7>::ParamType Param7; - typedef typename CrossThreadTaskTraits<P8>::ParamType Param8; - - static PassOwnPtr<CrossThreadTask> create(Method method, Param1 parameter1, Param2 parameter2, Param3 parameter3, Param4 parameter4, Param5 parameter5, Param6 parameter6, Param7 parameter7, Param8 parameter8) - { - return adoptPtr(new CrossThreadTask(method, parameter1, parameter2, parameter3, parameter4, parameter5, parameter6, parameter7, parameter8)); - } - -private: - CrossThreadTask8(Method method, Param1 parameter1, Param2 parameter2, Param3 parameter3, Param4 parameter4, Param5 parameter5, Param6 parameter6, Param7 parameter7, Param8 parameter8) - : m_method(method) - , m_parameter1(parameter1) - , m_parameter2(parameter2) - , m_parameter3(parameter3) - , m_parameter4(parameter4) - , m_parameter5(parameter5) - , m_parameter6(parameter6) - , m_parameter7(parameter7) - , m_parameter8(parameter8) - { - } - - virtual void performTask(ScriptExecutionContext* context) - { - (*m_method)(context, m_parameter1, m_parameter2, m_parameter3, m_parameter4, m_parameter5, m_parameter6, m_parameter7, m_parameter8); - } - -private: - Method m_method; - P1 m_parameter1; - P2 m_parameter2; - P3 m_parameter3; - P4 m_parameter4; - P5 m_parameter5; - P6 m_parameter6; - P7 m_parameter7; - P8 m_parameter8; -}; - -template<typename P1, typename MP1> -PassOwnPtr<ScriptExecutionContext::Task> createCallbackTask( - void (*method)(ScriptExecutionContext*, MP1), - const P1& parameter1) -{ - return CrossThreadTask1<typename CrossThreadCopier<P1>::Type, MP1>::create( - method, - CrossThreadCopier<P1>::copy(parameter1)); -} - -template<typename P1, typename MP1, typename P2, typename MP2> -PassOwnPtr<ScriptExecutionContext::Task> createCallbackTask( - void (*method)(ScriptExecutionContext*, MP1, MP2), - const P1& parameter1, const P2& parameter2) -{ - return CrossThreadTask2<typename CrossThreadCopier<P1>::Type, MP1, typename CrossThreadCopier<P2>::Type, MP2>::create( - method, - CrossThreadCopier<P1>::copy(parameter1), CrossThreadCopier<P2>::copy(parameter2)); -} - -template<typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3> -PassOwnPtr<ScriptExecutionContext::Task> createCallbackTask( - void (*method)(ScriptExecutionContext*, MP1, MP2, MP3), - const P1& parameter1, const P2& parameter2, const P3& parameter3) -{ - return CrossThreadTask3<typename CrossThreadCopier<P1>::Type, MP1, typename CrossThreadCopier<P2>::Type, MP2, typename CrossThreadCopier<P3>::Type, MP3>::create( - method, - CrossThreadCopier<P1>::copy(parameter1), CrossThreadCopier<P2>::copy(parameter2), - CrossThreadCopier<P3>::copy(parameter3)); -} - -template<typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3, typename P4, typename MP4> -PassOwnPtr<ScriptExecutionContext::Task> createCallbackTask( - void (*method)(ScriptExecutionContext*, MP1, MP2, MP3, MP4), - const P1& parameter1, const P2& parameter2, const P3& parameter3, const P4& parameter4) -{ - return CrossThreadTask4<typename CrossThreadCopier<P1>::Type, MP1, typename CrossThreadCopier<P2>::Type, MP2, typename CrossThreadCopier<P3>::Type, MP3, - typename CrossThreadCopier<P4>::Type, MP4>::create( - method, - CrossThreadCopier<P1>::copy(parameter1), CrossThreadCopier<P2>::copy(parameter2), - CrossThreadCopier<P3>::copy(parameter3), CrossThreadCopier<P4>::copy(parameter4)); -} - -template<typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3, typename P4, typename MP4, typename P5, typename MP5> -PassOwnPtr<ScriptExecutionContext::Task> createCallbackTask( - void (*method)(ScriptExecutionContext*, MP1, MP2, MP3, MP4, MP5), - const P1& parameter1, const P2& parameter2, const P3& parameter3, const P4& parameter4, const P5& parameter5) -{ - return CrossThreadTask5<typename CrossThreadCopier<P1>::Type, MP1, typename CrossThreadCopier<P2>::Type, MP2, typename CrossThreadCopier<P3>::Type, MP3, - typename CrossThreadCopier<P4>::Type, MP4, typename CrossThreadCopier<P5>::Type, MP5>::create( - method, - CrossThreadCopier<P1>::copy(parameter1), CrossThreadCopier<P2>::copy(parameter2), - CrossThreadCopier<P3>::copy(parameter3), CrossThreadCopier<P4>::copy(parameter4), - CrossThreadCopier<P5>::copy(parameter5)); -} - -template<typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3, typename P4, typename MP4, typename P5, typename MP5, typename P6, typename MP6> -PassOwnPtr<ScriptExecutionContext::Task> createCallbackTask( - void (*method)(ScriptExecutionContext*, MP1, MP2, MP3, MP4, MP5, MP6), - const P1& parameter1, const P2& parameter2, const P3& parameter3, const P4& parameter4, const P5& parameter5, const P6& parameter6) -{ - return CrossThreadTask6<typename CrossThreadCopier<P1>::Type, MP1, typename CrossThreadCopier<P2>::Type, MP2, typename CrossThreadCopier<P3>::Type, MP3, - typename CrossThreadCopier<P4>::Type, MP4, typename CrossThreadCopier<P5>::Type, MP5, typename CrossThreadCopier<P6>::Type, MP6>::create( - method, - CrossThreadCopier<P1>::copy(parameter1), CrossThreadCopier<P2>::copy(parameter2), - CrossThreadCopier<P3>::copy(parameter3), CrossThreadCopier<P4>::copy(parameter4), - CrossThreadCopier<P5>::copy(parameter5), CrossThreadCopier<P6>::copy(parameter6)); -} - -template<typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3, typename P4, typename MP4, typename P5, typename MP5, typename P6, typename MP6, typename P7, typename MP7> -PassOwnPtr<ScriptExecutionContext::Task> createCallbackTask( - void (*method)(ScriptExecutionContext*, MP1, MP2, MP3, MP4, MP5, MP6, MP7), - const P1& parameter1, const P2& parameter2, const P3& parameter3, const P4& parameter4, const P5& parameter5, const P6& parameter6, const P7& parameter7) -{ - return CrossThreadTask7<typename CrossThreadCopier<P1>::Type, MP1, typename CrossThreadCopier<P2>::Type, MP2, typename CrossThreadCopier<P3>::Type, MP3, - typename CrossThreadCopier<P4>::Type, MP4, typename CrossThreadCopier<P5>::Type, MP5, typename CrossThreadCopier<P6>::Type, MP6, - typename CrossThreadCopier<P7>::Type, MP7>::create( - method, - CrossThreadCopier<P1>::copy(parameter1), CrossThreadCopier<P2>::copy(parameter2), - CrossThreadCopier<P3>::copy(parameter3), CrossThreadCopier<P4>::copy(parameter4), - CrossThreadCopier<P5>::copy(parameter5), CrossThreadCopier<P6>::copy(parameter6), - CrossThreadCopier<P7>::copy(parameter7)); -} - -template<typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3, typename P4, typename MP4, typename P5, typename MP5, typename P6, typename MP6, typename P7, typename MP7, typename P8, typename MP8> -PassOwnPtr<ScriptExecutionContext::Task> createCallbackTask( - void (*method)(ScriptExecutionContext*, MP1, MP2, MP3, MP4, MP5, MP6, MP7, MP8), - const P1& parameter1, const P2& parameter2, const P3& parameter3, const P4& parameter4, const P5& parameter5, const P6& parameter6, const P7& parameter7, const P8& parameter8) -{ - return CrossThreadTask8<typename CrossThreadCopier<P1>::Type, MP1, typename CrossThreadCopier<P2>::Type, MP2, typename CrossThreadCopier<P3>::Type, MP3, - typename CrossThreadCopier<P4>::Type, MP4, typename CrossThreadCopier<P5>::Type, MP5, typename CrossThreadCopier<P6>::Type, MP6, - typename CrossThreadCopier<P7>::Type, MP7, typename CrossThreadCopier<P8>::Type, MP8>::create( - method, - CrossThreadCopier<P1>::copy(parameter1), CrossThreadCopier<P2>::copy(parameter2), - CrossThreadCopier<P3>::copy(parameter3), CrossThreadCopier<P4>::copy(parameter4), - CrossThreadCopier<P5>::copy(parameter5), CrossThreadCopier<P6>::copy(parameter6), - CrossThreadCopier<P7>::copy(parameter7), CrossThreadCopier<P8>::copy(parameter8)); -} - -} // namespace WebCore - -#endif // CrossThreadTask_h diff --git a/Source/WebCore/dom/CurrentScriptIncrementer.h b/Source/WebCore/dom/CurrentScriptIncrementer.h index a8ceca25d..d08902334 100644 --- a/Source/WebCore/dom/CurrentScriptIncrementer.h +++ b/Source/WebCore/dom/CurrentScriptIncrementer.h @@ -10,7 +10,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -26,8 +26,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef CurrentScriptIncrementer_h -#define CurrentScriptIncrementer_h +#pragma once #include "Document.h" #include "HTMLScriptElement.h" @@ -37,25 +36,27 @@ namespace WebCore { class CurrentScriptIncrementer { WTF_MAKE_NONCOPYABLE(CurrentScriptIncrementer); public: - CurrentScriptIncrementer(Document* document, Element* element) + CurrentScriptIncrementer(Document& document, Element& element) : m_document(document) - , m_isHTMLScriptElement(isHTMLScriptElement(element)) + , m_isHTMLScriptElement(is<HTMLScriptElement>(element)) { - if (m_isHTMLScriptElement) - m_document->pushCurrentScript(toHTMLScriptElement(element)); + if (!m_isHTMLScriptElement) + return; + auto& scriptElement = downcast<HTMLScriptElement>(element); + bool shouldPushNullForCurrentScript = scriptElement.isInShadowTree() || scriptElement.scriptType() == ScriptElement::ScriptType::Module; + m_document.pushCurrentScript(shouldPushNullForCurrentScript ? nullptr : &scriptElement); } ~CurrentScriptIncrementer() { - if (m_isHTMLScriptElement) - m_document->popCurrentScript(); + if (!m_isHTMLScriptElement) + return; + m_document.popCurrentScript(); } private: - Document* m_document; + Document& m_document; bool m_isHTMLScriptElement; }; -} - -#endif +} // namespace WebCore diff --git a/Source/WebCore/dom/CustomElementReactionQueue.cpp b/Source/WebCore/dom/CustomElementReactionQueue.cpp new file mode 100644 index 000000000..a7dab97ad --- /dev/null +++ b/Source/WebCore/dom/CustomElementReactionQueue.cpp @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "CustomElementReactionQueue.h" + +#include "CustomElementRegistry.h" +#include "DOMWindow.h" +#include "Document.h" +#include "Element.h" +#include "HTMLNames.h" +#include "JSCustomElementInterface.h" +#include "JSDOMBinding.h" +#include "Microtasks.h" +#include <heap/Heap.h> +#include <wtf/Optional.h> +#include <wtf/Ref.h> +#include <wtf/SetForScope.h> + +namespace WebCore { + +class CustomElementReactionQueueItem { +public: + enum class Type { + ElementUpgrade, + Connected, + Disconnected, + Adopted, + AttributeChanged, + }; + + CustomElementReactionQueueItem(Type type) + : m_type(type) + { } + + CustomElementReactionQueueItem(Document& oldDocument, Document& newDocument) + : m_type(Type::Adopted) + , m_oldDocument(&oldDocument) + , m_newDocument(&newDocument) + { } + + CustomElementReactionQueueItem(const QualifiedName& attributeName, const AtomicString& oldValue, const AtomicString& newValue) + : m_type(Type::AttributeChanged) + , m_attributeName(attributeName) + , m_oldValue(oldValue) + , m_newValue(newValue) + { } + + void invoke(Element& element, JSCustomElementInterface& elementInterface) + { + switch (m_type) { + case Type::ElementUpgrade: + elementInterface.upgradeElement(element); + break; + case Type::Connected: + elementInterface.invokeConnectedCallback(element); + break; + case Type::Disconnected: + elementInterface.invokeDisconnectedCallback(element); + break; + case Type::Adopted: + elementInterface.invokeAdoptedCallback(element, *m_oldDocument, *m_newDocument); + break; + case Type::AttributeChanged: + ASSERT(m_attributeName); + elementInterface.invokeAttributeChangedCallback(element, m_attributeName.value(), m_oldValue, m_newValue); + break; + } + } + +private: + Type m_type; + RefPtr<Document> m_oldDocument; + RefPtr<Document> m_newDocument; + std::optional<QualifiedName> m_attributeName; + AtomicString m_oldValue; + AtomicString m_newValue; +}; + +CustomElementReactionQueue::CustomElementReactionQueue(JSCustomElementInterface& elementInterface) + : m_interface(elementInterface) +{ } + +CustomElementReactionQueue::~CustomElementReactionQueue() +{ + ASSERT(m_items.isEmpty()); +} + +void CustomElementReactionQueue::clear() +{ + m_items.clear(); +} + +void CustomElementReactionQueue::enqueueElementUpgrade(Element& element) +{ + auto& queue = CustomElementReactionStack::ensureCurrentQueue(element); + queue.m_items.append({CustomElementReactionQueueItem::Type::ElementUpgrade}); +} + +void CustomElementReactionQueue::enqueueElementUpgradeIfDefined(Element& element) +{ + ASSERT(element.isConnected()); + ASSERT(element.isCustomElementUpgradeCandidate()); + auto* window = element.document().domWindow(); + if (!window) + return; + + auto* registry = window->customElementRegistry(); + if (!registry) + return; + + auto* elementInterface = registry->findInterface(element); + if (!elementInterface) + return; + + element.enqueueToUpgrade(*elementInterface); +} + +void CustomElementReactionQueue::enqueueConnectedCallbackIfNeeded(Element& element) +{ + ASSERT(element.isDefinedCustomElement()); + ASSERT(element.document().refCount() > 0); + auto& queue = CustomElementReactionStack::ensureCurrentQueue(element); + if (queue.m_interface->hasConnectedCallback()) + queue.m_items.append({CustomElementReactionQueueItem::Type::Connected}); +} + +void CustomElementReactionQueue::enqueueDisconnectedCallbackIfNeeded(Element& element) +{ + ASSERT(element.isDefinedCustomElement()); + if (element.document().refCount() <= 0) + return; // Don't enqueue disconnectedCallback if the entire document is getting destructed. + auto& queue = CustomElementReactionStack::ensureCurrentQueue(element); + if (queue.m_interface->hasDisconnectedCallback()) + queue.m_items.append({CustomElementReactionQueueItem::Type::Disconnected}); +} + +void CustomElementReactionQueue::enqueueAdoptedCallbackIfNeeded(Element& element, Document& oldDocument, Document& newDocument) +{ + ASSERT(element.isDefinedCustomElement()); + ASSERT(element.document().refCount() > 0); + auto& queue = CustomElementReactionStack::ensureCurrentQueue(element); + if (queue.m_interface->hasAdoptedCallback()) + queue.m_items.append({oldDocument, newDocument}); +} + +void CustomElementReactionQueue::enqueueAttributeChangedCallbackIfNeeded(Element& element, const QualifiedName& attributeName, const AtomicString& oldValue, const AtomicString& newValue) +{ + ASSERT(element.isDefinedCustomElement()); + ASSERT(element.document().refCount() > 0); + auto& queue = CustomElementReactionStack::ensureCurrentQueue(element); + if (queue.m_interface->observesAttribute(attributeName.localName())) + queue.m_items.append({attributeName, oldValue, newValue}); +} + +void CustomElementReactionQueue::enqueuePostUpgradeReactions(Element& element) +{ + ASSERT(element.isCustomElementUpgradeCandidate()); + if (!element.hasAttributes() && !element.isConnected()) + return; + + auto* queue = element.reactionQueue(); + ASSERT(queue); + + if (element.hasAttributes()) { + for (auto& attribute : element.attributesIterator()) { + if (queue->m_interface->observesAttribute(attribute.localName())) + queue->m_items.append({attribute.name(), nullAtom, attribute.value()}); + } + } + + if (element.isConnected() && queue->m_interface->hasConnectedCallback()) + queue->m_items.append({CustomElementReactionQueueItem::Type::Connected}); +} + +bool CustomElementReactionQueue::observesStyleAttribute() const +{ + return m_interface->observesAttribute(HTMLNames::styleAttr.localName()); +} + +void CustomElementReactionQueue::invokeAll(Element& element) +{ + while (!m_items.isEmpty()) { + Vector<CustomElementReactionQueueItem> items = WTFMove(m_items); + for (auto& item : items) + item.invoke(element, m_interface.get()); + } +} + +inline void CustomElementReactionStack::ElementQueue::add(Element& element) +{ + RELEASE_ASSERT(!m_invoking); + // FIXME: Avoid inserting the same element multiple times. + m_elements.append(element); +} + +inline void CustomElementReactionStack::ElementQueue::invokeAll() +{ + RELEASE_ASSERT(!m_invoking); + SetForScope<bool> invoking(m_invoking, true); + Vector<Ref<Element>> elements; + elements.swap(m_elements); + RELEASE_ASSERT(m_elements.isEmpty()); + for (auto& element : elements) { + auto* queue = element->reactionQueue(); + ASSERT(queue); + queue->invokeAll(element.get()); + } + RELEASE_ASSERT(m_elements.isEmpty()); +} + +CustomElementReactionQueue& CustomElementReactionStack::ensureCurrentQueue(Element& element) +{ + ASSERT(element.reactionQueue()); + if (!s_currentProcessingStack) { + auto& queue = CustomElementReactionStack::ensureBackupQueue(); + queue.add(element); + return *element.reactionQueue(); + } + + auto*& queue = s_currentProcessingStack->m_queue; + if (!queue) // We use a raw pointer to avoid genearing code to delete it in ~CustomElementReactionStack. + queue = new ElementQueue; + queue->add(element); + return *element.reactionQueue(); +} + +CustomElementReactionStack* CustomElementReactionStack::s_currentProcessingStack = nullptr; + +void CustomElementReactionStack::processQueue() +{ + ASSERT(m_queue); + m_queue->invokeAll(); + delete m_queue; + m_queue = nullptr; +} + +class BackupElementQueueMicrotask final : public Microtask { + WTF_MAKE_FAST_ALLOCATED; +private: + Result run() final + { + CustomElementReactionStack::processBackupQueue(); + return Result::Done; + } +}; + +static bool s_processingBackupElementQueue = false; + +CustomElementReactionStack::ElementQueue& CustomElementReactionStack::ensureBackupQueue() +{ + if (!s_processingBackupElementQueue) { + s_processingBackupElementQueue = true; + MicrotaskQueue::mainThreadQueue().append(std::make_unique<BackupElementQueueMicrotask>()); + } + return backupElementQueue(); +} + +void CustomElementReactionStack::processBackupQueue() +{ + backupElementQueue().invokeAll(); + s_processingBackupElementQueue = false; +} + +CustomElementReactionStack::ElementQueue& CustomElementReactionStack::backupElementQueue() +{ + static NeverDestroyed<ElementQueue> queue; + return queue.get(); +} + +} diff --git a/Source/WebCore/dom/CustomElementReactionQueue.h b/Source/WebCore/dom/CustomElementReactionQueue.h new file mode 100644 index 000000000..0c9123dc2 --- /dev/null +++ b/Source/WebCore/dom/CustomElementReactionQueue.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <wtf/Forward.h> +#include <wtf/Noncopyable.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class CustomElementReactionQueueItem; +class Document; +class Element; +class JSCustomElementInterface; +class QualifiedName; + +class CustomElementReactionQueue { + WTF_MAKE_NONCOPYABLE(CustomElementReactionQueue); +public: + CustomElementReactionQueue(JSCustomElementInterface&); + ~CustomElementReactionQueue(); + + static void enqueueElementUpgrade(Element&); + static void enqueueElementUpgradeIfDefined(Element&); + static void enqueueConnectedCallbackIfNeeded(Element&); + static void enqueueDisconnectedCallbackIfNeeded(Element&); + static void enqueueAdoptedCallbackIfNeeded(Element&, Document& oldDocument, Document& newDocument); + static void enqueueAttributeChangedCallbackIfNeeded(Element&, const QualifiedName&, const AtomicString& oldValue, const AtomicString& newValue); + static void enqueuePostUpgradeReactions(Element&); + + bool observesStyleAttribute() const; + void invokeAll(Element&); + void clear(); + +private: + Ref<JSCustomElementInterface> m_interface; + Vector<CustomElementReactionQueueItem> m_items; +}; + +class CustomElementReactionStack { +public: + CustomElementReactionStack() + : m_previousProcessingStack(s_currentProcessingStack) + { + s_currentProcessingStack = this; + } + + ~CustomElementReactionStack() + { + if (UNLIKELY(m_queue)) + processQueue(); + s_currentProcessingStack = m_previousProcessingStack; + } + + static CustomElementReactionQueue& ensureCurrentQueue(Element&); + + static bool hasCurrentProcessingStack() { return s_currentProcessingStack; } + + static void processBackupQueue(); + +private: + class ElementQueue { + public: + void add(Element&); + void invokeAll(); + + private: + Vector<Ref<Element>> m_elements; + bool m_invoking { false }; + }; + + WEBCORE_EXPORT void processQueue(); + + static ElementQueue& ensureBackupQueue(); + static ElementQueue& backupElementQueue(); + + ElementQueue* m_queue { nullptr }; + CustomElementReactionStack* m_previousProcessingStack; + + WEBCORE_EXPORT static CustomElementReactionStack* s_currentProcessingStack; +}; + +} diff --git a/Source/WebCore/dom/CustomElementRegistry.cpp b/Source/WebCore/dom/CustomElementRegistry.cpp new file mode 100644 index 000000000..f2f108b80 --- /dev/null +++ b/Source/WebCore/dom/CustomElementRegistry.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "CustomElementRegistry.h" + +#include "CustomElementReactionQueue.h" +#include "DOMWindow.h" +#include "Document.h" +#include "Element.h" +#include "ElementTraversal.h" +#include "JSCustomElementInterface.h" +#include "MathMLNames.h" +#include "QualifiedName.h" +#include "ShadowRoot.h" +#include <runtime/JSCJSValueInlines.h> +#include <wtf/text/AtomicString.h> + +namespace WebCore { + +Ref<CustomElementRegistry> CustomElementRegistry::create(DOMWindow& window) +{ + return adoptRef(*new CustomElementRegistry(window)); +} + +CustomElementRegistry::CustomElementRegistry(DOMWindow& window) + : m_window(window) +{ } + +CustomElementRegistry::~CustomElementRegistry() +{ } + +// https://dom.spec.whatwg.org/#concept-shadow-including-tree-order +static void enqueueUpgradeInShadowIncludingTreeOrder(ContainerNode& node, JSCustomElementInterface& elementInterface) +{ + for (Element* element = ElementTraversal::firstWithin(node); element; element = ElementTraversal::next(*element)) { + if (element->isCustomElementUpgradeCandidate() && element->tagQName() == elementInterface.name()) + element->enqueueToUpgrade(elementInterface); + if (auto* shadowRoot = element->shadowRoot()) { + if (shadowRoot->mode() != ShadowRootMode::UserAgent) + enqueueUpgradeInShadowIncludingTreeOrder(*shadowRoot, elementInterface); + } + } +} + +void CustomElementRegistry::addElementDefinition(Ref<JSCustomElementInterface>&& elementInterface) +{ + AtomicString localName = elementInterface->name().localName(); + ASSERT(!m_nameMap.contains(localName)); + m_constructorMap.add(elementInterface->constructor(), elementInterface.ptr()); + m_nameMap.add(localName, elementInterface.copyRef()); + + if (auto* document = m_window.document()) + enqueueUpgradeInShadowIncludingTreeOrder(*document, elementInterface.get()); + + if (auto promise = m_promiseMap.take(localName)) + promise.value()->resolve(); +} + +JSCustomElementInterface* CustomElementRegistry::findInterface(const Element& element) const +{ + return findInterface(element.tagQName()); +} + +JSCustomElementInterface* CustomElementRegistry::findInterface(const QualifiedName& name) const +{ + if (name.namespaceURI() != HTMLNames::xhtmlNamespaceURI) + return nullptr; + return m_nameMap.get(name.localName()); +} + +JSCustomElementInterface* CustomElementRegistry::findInterface(const AtomicString& name) const +{ + return m_nameMap.get(name); +} + +JSCustomElementInterface* CustomElementRegistry::findInterface(const JSC::JSObject* constructor) const +{ + return m_constructorMap.get(constructor); +} + +bool CustomElementRegistry::containsConstructor(const JSC::JSObject* constructor) const +{ + return m_constructorMap.contains(constructor); +} + +JSC::JSValue CustomElementRegistry::get(const AtomicString& name) +{ + if (auto* elementInterface = m_nameMap.get(name)) + return elementInterface->constructor(); + return JSC::jsUndefined(); +} + +} diff --git a/Source/WebCore/dom/CustomElementRegistry.h b/Source/WebCore/dom/CustomElementRegistry.h new file mode 100644 index 000000000..112097b1c --- /dev/null +++ b/Source/WebCore/dom/CustomElementRegistry.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "JSDOMPromise.h" +#include "QualifiedName.h" +#include <wtf/HashMap.h> +#include <wtf/SetForScope.h> +#include <wtf/text/AtomicString.h> +#include <wtf/text/AtomicStringHash.h> + +namespace JSC { + +class JSObject; +class JSValue; + +} + +namespace WebCore { + +class CustomElementRegistry; +class DOMWindow; +class Element; +class JSCustomElementInterface; +class QualifiedName; + +class CustomElementRegistry : public RefCounted<CustomElementRegistry> { +public: + static Ref<CustomElementRegistry> create(DOMWindow&); + ~CustomElementRegistry(); + + void addElementDefinition(Ref<JSCustomElementInterface>&&); + + bool& elementDefinitionIsRunning() { return m_elementDefinitionIsRunning; } + + JSCustomElementInterface* findInterface(const Element&) const; + JSCustomElementInterface* findInterface(const QualifiedName&) const; + JSCustomElementInterface* findInterface(const AtomicString&) const; + JSCustomElementInterface* findInterface(const JSC::JSObject*) const; + bool containsConstructor(const JSC::JSObject*) const; + + JSC::JSValue get(const AtomicString&); + + HashMap<AtomicString, Ref<DeferredPromise>>& promiseMap() { return m_promiseMap; } + +private: + CustomElementRegistry(DOMWindow&); + + DOMWindow& m_window; + HashMap<AtomicString, Ref<JSCustomElementInterface>> m_nameMap; + HashMap<const JSC::JSObject*, JSCustomElementInterface*> m_constructorMap; + HashMap<AtomicString, Ref<DeferredPromise>> m_promiseMap; + + bool m_elementDefinitionIsRunning { false }; + + friend class ElementDefinitionIsRunningSetForScope; +}; + +} diff --git a/Source/WebCore/dom/CustomElementRegistry.idl b/Source/WebCore/dom/CustomElementRegistry.idl new file mode 100644 index 000000000..c52533bf5 --- /dev/null +++ b/Source/WebCore/dom/CustomElementRegistry.idl @@ -0,0 +1,34 @@ +/* +* Copyright (C) 2016 Apple Inc. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +[ + EnabledAtRuntime=CustomElements, + ImplementationLacksVTable, + JSGenerateToNativeObject, +] interface CustomElementRegistry { + [CEReactions, Custom] void define(DOMString name, Function constructor); + any get(DOMString name); + [Custom, MayThrowException] Promise<void> whenDefined(DOMString name); +}; diff --git a/Source/WebCore/dom/CustomEvent.cpp b/Source/WebCore/dom/CustomEvent.cpp index d5ef0bdee..080cab456 100644 --- a/Source/WebCore/dom/CustomEvent.cpp +++ b/Source/WebCore/dom/CustomEvent.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,21 +26,18 @@ #include "config.h" #include "CustomEvent.h" -#include "EventNames.h" +#include <runtime/JSCInlines.h> namespace WebCore { -CustomEventInit::CustomEventInit() +CustomEvent::CustomEvent(IsTrusted isTrusted) + : Event(isTrusted) { } -CustomEvent::CustomEvent() -{ -} - -CustomEvent::CustomEvent(const AtomicString& type, const CustomEventInit& initializer) - : Event(type, initializer) - , m_detail(initializer.detail) +CustomEvent::CustomEvent(JSC::ExecState& state, const AtomicString& type, const Init& initializer, IsTrusted isTrusted) + : Event(type, initializer, isTrusted) + , m_detail(state.vm(), initializer.detail) { } @@ -48,15 +45,25 @@ CustomEvent::~CustomEvent() { } -void CustomEvent::initCustomEvent(const AtomicString& type, bool canBubble, bool cancelable, const Deprecated::ScriptValue& detail) +void CustomEvent::initCustomEvent(JSC::ExecState& state, const AtomicString& type, bool canBubble, bool cancelable, JSC::JSValue detail) { - ASSERT(!m_serializedScriptValue.get()); - if (dispatched()) + if (isBeingDispatched()) return; initEvent(type, canBubble, cancelable); - m_detail = detail; + m_detail = { state.vm(), detail }; + m_serializedDetail = nullptr; + m_triedToSerialize = false; +} + +RefPtr<SerializedScriptValue> CustomEvent::trySerializeDetail(JSC::ExecState& state) +{ + if (!m_triedToSerialize) { + m_serializedDetail = SerializedScriptValue::create(state, m_detail, SerializationErrorMode::NonThrowing); + m_triedToSerialize = true; + } + return m_serializedDetail; } EventInterface CustomEvent::eventInterface() const diff --git a/Source/WebCore/dom/CustomEvent.h b/Source/WebCore/dom/CustomEvent.h index 7f4c1cf04..483495f1e 100644 --- a/Source/WebCore/dom/CustomEvent.h +++ b/Source/WebCore/dom/CustomEvent.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef CustomEvent_h -#define CustomEvent_h +#pragma once #include "Event.h" #include "SerializedScriptValue.h" @@ -32,41 +31,39 @@ namespace WebCore { -struct CustomEventInit : public EventInit { - CustomEventInit(); - - Deprecated::ScriptValue detail; -}; - -class CustomEvent : public Event { +class CustomEvent final : public Event { public: virtual ~CustomEvent(); - static PassRefPtr<CustomEvent> create() + static Ref<CustomEvent> create(IsTrusted isTrusted = IsTrusted::No) { - return adoptRef(new CustomEvent); + return adoptRef(*new CustomEvent(isTrusted)); } - static PassRefPtr<CustomEvent> create(const AtomicString& type, const CustomEventInit& initializer) + struct Init : EventInit { + JSC::JSValue detail; + }; + + static Ref<CustomEvent> create(JSC::ExecState& state, const AtomicString& type, const Init& initializer, IsTrusted isTrusted = IsTrusted::No) { - return adoptRef(new CustomEvent(type, initializer)); + return adoptRef(*new CustomEvent(state, type, initializer, isTrusted)); } - void initCustomEvent(const AtomicString& type, bool canBubble, bool cancelable, const Deprecated::ScriptValue& detail); + void initCustomEvent(JSC::ExecState&, const AtomicString& type, bool canBubble, bool cancelable, JSC::JSValue detail = JSC::JSValue::JSUndefined); - virtual EventInterface eventInterface() const; + EventInterface eventInterface() const override; - const Deprecated::ScriptValue& detail() const { return m_detail; } - PassRefPtr<SerializedScriptValue> serializedScriptValue() { return m_serializedScriptValue; } + JSC::JSValue detail() const { return m_detail.jsValue(); } + + RefPtr<SerializedScriptValue> trySerializeDetail(JSC::ExecState&); private: - CustomEvent(); - CustomEvent(const AtomicString& type, const CustomEventInit& initializer); + CustomEvent(IsTrusted); + CustomEvent(JSC::ExecState&, const AtomicString& type, const Init& initializer, IsTrusted); - Deprecated::ScriptValue m_detail; - RefPtr<SerializedScriptValue> m_serializedScriptValue; + Deprecated::ScriptValue m_detail; // FIXME: Why is it OK to use a strong reference here? What prevents a reference cycle? + RefPtr<SerializedScriptValue> m_serializedDetail; + bool m_triedToSerialize { false }; }; } // namespace WebCore - -#endif // CustomEvent_h diff --git a/Source/WebCore/dom/CustomEvent.idl b/Source/WebCore/dom/CustomEvent.idl index 17dce6f33..1866be4a2 100644 --- a/Source/WebCore/dom/CustomEvent.idl +++ b/Source/WebCore/dom/CustomEvent.idl @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -23,17 +23,17 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#if !defined(LANGUAGE_CPP) || !LANGUAGE_CPP // Introduced in DOM Level 3: +// FIXME: This should be exposed to workers as well. [ - ConstructorTemplate=Event, + Constructor(DOMString type, optional CustomEventInit eventInitDict), + ConstructorCallWith=ScriptState, ] interface CustomEvent : Event { - [InitializedByEventConstructor] readonly attribute any detail; + [CustomGetter] readonly attribute any detail; - void initCustomEvent([Default=Undefined] optional DOMString typeArg, - [Default=Undefined] optional boolean canBubbleArg, - [Default=Undefined] optional boolean cancelableArg, - [Default=Undefined] optional any detailArg); + [CallWith=ScriptState] void initCustomEvent(DOMString type, optional boolean bubbles = false, optional boolean cancelable = false, optional any detail = null); }; -#endif +dictionary CustomEventInit : EventInit { + any detail = null; +}; diff --git a/Source/WebCore/dom/DOMAllInOne.cpp b/Source/WebCore/dom/DOMAllInOne.cpp new file mode 100644 index 000000000..a9760ffcb --- /dev/null +++ b/Source/WebCore/dom/DOMAllInOne.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2010, 2011 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// This all-in-one cpp file cuts down on template bloat to allow us to build our Windows release build. + +#include "ActiveDOMCallbackMicrotask.cpp" +#include "ActiveDOMObject.cpp" +#include "AnimationEvent.cpp" +#include "Attr.cpp" +#include "StyleScope.cpp" +#include "BeforeTextInsertedEvent.cpp" +#include "BeforeUnloadEvent.cpp" +#include "CDATASection.cpp" +#include "CharacterData.cpp" +#include "ChildListMutationScope.cpp" +#include "ChildNodeList.cpp" +#include "ClassCollection.cpp" +#include "ClientRect.cpp" +#include "ClientRectList.cpp" +#include "ClipboardEvent.cpp" +#include "CollectionIndexCache.cpp" +#include "Comment.cpp" +#include "ComposedTreeIterator.cpp" +#include "CompositionEvent.cpp" +#include "ContainerNode.cpp" +#include "ContainerNodeAlgorithms.cpp" +#include "ContextDestructionObserver.cpp" +#include "CustomEvent.cpp" +#include "DOMCoreException.cpp" +#include "DOMError.cpp" +#include "DOMImplementation.cpp" +#include "DOMNamedFlowCollection.cpp" +#include "DOMStringList.cpp" +#include "DataTransfer.cpp" +#include "DataTransferItem.cpp" +#include "DatasetDOMStringMap.cpp" +#include "DecodedDataDocumentParser.cpp" +#include "DeviceMotionController.cpp" +#include "DeviceMotionData.cpp" +#include "DeviceMotionEvent.cpp" +#include "DeviceOrientationController.cpp" +#include "DeviceOrientationData.cpp" +#include "DeviceOrientationEvent.cpp" +#include "Document.cpp" +#include "DocumentEventQueue.cpp" +#include "DocumentFragment.cpp" +#include "DocumentMarkerController.cpp" +#include "DocumentOrderedMap.cpp" +#include "DocumentParser.cpp" +#include "DocumentSharedObjectPool.cpp" +#include "DocumentType.cpp" +#include "Element.cpp" +#include "ElementData.cpp" +#include "ElementRareData.cpp" +#include "ErrorEvent.cpp" +#include "Event.cpp" +#include "EventContext.cpp" +#include "EventDispatcher.cpp" +#include "EventListenerMap.cpp" +#include "EventNames.cpp" +#include "EventPath.cpp" +#include "EventTarget.cpp" +#include "ExceptionBase.cpp" +#include "ExtensionStyleSheets.cpp" +#include "FocusEvent.cpp" +#include "GenericEventQueue.cpp" +#include "IdTargetObserver.cpp" +#include "IdTargetObserverRegistry.cpp" +#include "InlineStyleSheetOwner.cpp" +#include "InputEvent.cpp" +#include "KeyboardEvent.cpp" +#include "CustomElementReactionQueue.cpp" +#include "LiveNodeList.cpp" +#include "MessageChannel.cpp" +#include "MessageEvent.cpp" +#include "MessagePort.cpp" +#include "Microtasks.cpp" +#include "MouseEvent.cpp" +#include "MouseRelatedEvent.cpp" +#include "MutationEvent.cpp" +#include "MutationObserver.cpp" +#include "MutationObserverInterestGroup.cpp" +#include "MutationObserverRegistration.cpp" +#include "MutationRecord.cpp" +#include "NameNodeList.cpp" +#include "NamedFlowCollection.cpp" +#include "NamedNodeMap.cpp" +#include "Node.cpp" +#include "NodeFilterCondition.cpp" +#include "NodeIterator.cpp" +#include "NodeRareData.cpp" +#include "NodeTraversal.cpp" +#include "OverflowEvent.cpp" +#include "PageTransitionEvent.cpp" +#include "PendingScript.cpp" +#include "PopStateEvent.cpp" +#include "Position.cpp" +#include "PositionIterator.cpp" +#include "ProcessingInstruction.cpp" +#include "ProgressEvent.cpp" +#include "PseudoElement.cpp" +// Build error if adding QualifiedName.cpp to DOMAllInOne.cpp +// https://bugs.webkit.org/show_bug.cgi?id=146586 +// #include "QualifiedName.cpp" +#include "RadioButtonGroups.cpp" +#include "Range.cpp" +#include "ScopedEventQueue.cpp" +#include "ScriptElement.cpp" +#include "ScriptExecutionContext.cpp" +#include "ScriptRunner.cpp" +#include "ScriptableDocumentParser.cpp" +#include "ScriptedAnimationController.cpp" +#include "SecurityContext.cpp" +#include "SecurityOriginPolicy.cpp" +#include "SelectorQuery.cpp" +#include "ShadowRoot.cpp" +#include "SlotAssignment.cpp" +#include "SpaceSplitString.cpp" +#include "StaticNodeList.cpp" +#include "StaticRange.cpp" +#include "StringCallback.cpp" +#include "StyledElement.cpp" +#include "TagCollection.cpp" +#include "Text.cpp" +#include "TextEvent.cpp" +#include "TextNodeTraversal.cpp" +#include "Touch.cpp" +#include "TouchEvent.cpp" +#include "TouchList.cpp" +#include "TransformSourceLibxslt.cpp" +#include "TransitionEvent.cpp" +#include "Traversal.cpp" +#include "TreeScope.cpp" +#include "TreeScopeAdopter.cpp" +#include "TreeWalker.cpp" +#include "UIEvent.cpp" +#include "UIEventWithKeyState.cpp" +#include "UserActionElementSet.cpp" +#include "UserGestureIndicator.cpp" +#include "UserTypingGestureIndicator.cpp" +#include "ViewportArguments.cpp" +#include "VisitedLinkState.cpp" +#include "WebKitAnimationEvent.cpp" +#include "WebKitNamedFlow.cpp" +#include "WebKitTransitionEvent.cpp" +#include "WheelEvent.cpp" +#include "XMLDocumentParser.cpp" +#include "XMLDocumentParserScope.cpp" + diff --git a/Source/WebCore/dom/DOMCoreException.cpp b/Source/WebCore/dom/DOMCoreException.cpp index 6b1f0c507..3756c1b78 100644 --- a/Source/WebCore/dom/DOMCoreException.cpp +++ b/Source/WebCore/dom/DOMCoreException.cpp @@ -10,7 +10,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -29,40 +29,73 @@ #include "config.h" #include "DOMCoreException.h" +#include "ExceptionCode.h" +#include "ExceptionCodeDescription.h" + namespace WebCore { -static struct CoreException { +// http://heycam.github.io/webidl/#idl-DOMException-error-names +static const struct CoreException { const char* const name; const char* const description; + ExceptionCode code; } coreExceptions[] = { - { "IndexSizeError", "Index or size was negative, or greater than the allowed value." }, - { 0, 0 }, // DOMStringSizeError - { "HierarchyRequestError", "A Node was inserted somewhere it doesn't belong." }, - { "WrongDocumentError", "A Node was used in a different document than the one that created it (that doesn't support it)." }, - { "InvalidCharacterError", "An invalid or illegal character was specified, such as in an XML name." }, - { 0, 0 }, // NoDataAllowedError - { "NoModificationAllowedError", "An attempt was made to modify an object where modifications are not allowed." }, - { "NotFoundError", "An attempt was made to reference a Node in a context where it does not exist." }, - { "NotSupportedError", "The implementation did not support the requested type of object or operation." }, - { "InUseAttributeError", "An attempt was made to add an attribute that is already in use elsewhere." }, - { "InvalidStateError", "An attempt was made to use an object that is not, or is no longer, usable." }, - { "SyntaxError", "An invalid or illegal string was specified." }, - { "InvalidModificationError", "An attempt was made to modify the type of the underlying object." }, - { "NamespaceError", "An attempt was made to create or change an object in a way which is incorrect with regard to namespaces." }, - { "InvalidAccessError", "A parameter or an operation was not supported by the underlying object." }, - { 0, 0 }, // ValidationError - { "TypeMismatchError", "The type of an object was incompatible with the expected type of the parameter associated to the object." }, - { "SecurityError", "An attempt was made to break through the security policy of the user agent." }, - // FIXME: Couldn't find a description in the HTML/DOM specifications for NETWORK_ERR, ABORT_ERR, URL_MISMATCH_ERR, and QUOTA_EXCEEDED_ERR - { "NetworkError", "A network error occurred." }, - { "AbortError", "The user aborted a request." }, - { "URLMismatchError", "A worker global scope represented an absolute URL that is not equal to the resulting absolute URL." }, - { "QuotaExceededError", "An attempt was made to add something to storage that exceeded the quota." }, - { "TimeoutError", "A timeout occurred." }, - { "InvalidNodeTypeError", "The supplied node is invalid or has an invalid ancestor for this operation." }, - { "DataCloneError", "An object could not be cloned." } + { "IndexSizeError", "The index is not in the allowed range.", 1 }, + { nullptr, nullptr, 0 }, // DOMStringSizeError + { "HierarchyRequestError", "The operation would yield an incorrect node tree.", 3 }, + { "WrongDocumentError", "The object is in the wrong document.", 4 }, + { "InvalidCharacterError", "The string contains invalid characters.", 5 }, + { nullptr, nullptr, 0 }, // NoDataAllowedError + { "NoModificationAllowedError", "The object can not be modified.", 7 }, + { "NotFoundError", "The object can not be found here.", 8 }, + { "NotSupportedError", "The operation is not supported.", 9 }, + { "InUseAttributeError", "The attribute is in use.", 10 }, + { "InvalidStateError", "The object is in an invalid state.", 11 }, + { "SyntaxError", "The string did not match the expected pattern.", 12 }, + { "InvalidModificationError", " The object can not be modified in this way.", 13 }, + { "NamespaceError", "The operation is not allowed by Namespaces in XML.", 14 }, + { "InvalidAccessError", "The object does not support the operation or argument.", 15 }, + { nullptr, nullptr, 0 }, // ValidationError + { "TypeMismatchError", "The type of an object was incompatible with the expected type of the parameter associated to the object.", 17 }, + { "SecurityError", "The operation is insecure.", 18 }, + { "NetworkError", " A network error occurred.", 19 }, + { "AbortError", "The operation was aborted.", 20 }, + { "URLMismatchError", "The given URL does not match another URL.", 21 }, + { "QuotaExceededError", "The quota has been exceeded.", 22 }, + { "TimeoutError", "The operation timed out.", 23 }, + { "InvalidNodeTypeError", "The supplied node is incorrect or has an incorrect ancestor for this operation.", 24 }, + { "DataCloneError", "The object can not be cloned.", 25 }, + { "EncodingError", "The encoding operation (either encoded or decoding) failed.", 0 }, + { "NotReadableError", "The I/O read operation failed.", 0 }, + { "UnknownError", "The operation failed for an unknown transient reason (e.g. out of memory).", 0 }, + { "ConstraintError", "A mutation operation in a transaction failed because a constraint was not satisfied.", 0 }, + { "DataError", "Provided data is inadequate.", 0 }, + { "TransactionInactiveError", "A request was placed against a transaction which is currently not active, or which is finished.", 0 }, + { "ReadOnlyError", "The mutating operation was attempted in a \"readonly\" transaction.", 0 }, + { "VersionError", "An attempt was made to open a database using a lower version than the existing version.", 0 }, + { "OperationError", "The operation failed for an operation-specific reason.", 0 }, + { "NotAllowedError", "The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.", 0 } }; +static ExceptionCode errorCodeFromName(const String& name) +{ + for (auto& entry : coreExceptions) { + if (entry.name == name) + return entry.code; + } + return 0; +} + +Ref<DOMCoreException> DOMCoreException::create(const String& message, const String& name) +{ + return adoptRef(*new DOMCoreException(errorCodeFromName(name), message, name)); +} + +DOMCoreException::DOMCoreException(ExceptionCode ec, const String& message, const String& name) + : ExceptionBase(ec, name, message, ASCIILiteral("DOM")) +{ +} + bool DOMCoreException::initializeDescription(ExceptionCode ec, ExceptionCodeDescription* description) { description->typeName = "DOM"; diff --git a/Source/WebCore/dom/DOMCoreException.h b/Source/WebCore/dom/DOMCoreException.h index cc21a7a14..87b421d6c 100644 --- a/Source/WebCore/dom/DOMCoreException.h +++ b/Source/WebCore/dom/DOMCoreException.h @@ -10,7 +10,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -26,8 +26,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DOMCoreException_h -#define DOMCoreException_h +#pragma once #include "ExceptionBase.h" @@ -35,14 +34,16 @@ namespace WebCore { class DOMCoreException : public ExceptionBase { public: - static PassRefPtr<DOMCoreException> create(const ExceptionCodeDescription& description) + static Ref<DOMCoreException> create(const ExceptionCodeDescription& description) { - return adoptRef(new DOMCoreException(description)); + return adoptRef(*new DOMCoreException(description)); } + static Ref<DOMCoreException> create(const String& message, const String& name); static bool initializeDescription(ExceptionCode, ExceptionCodeDescription*); -private: +protected: + DOMCoreException(ExceptionCode, const String& message, const String& name); explicit DOMCoreException(const ExceptionCodeDescription& description) : ExceptionBase(description) { @@ -50,5 +51,3 @@ private: }; } // namespace WebCore - -#endif // DOMCoreException_h diff --git a/Source/WebCore/dom/DOMCoreException.idl b/Source/WebCore/dom/DOMCoreException.idl index 5215ed913..34d150f51 100644 --- a/Source/WebCore/dom/DOMCoreException.idl +++ b/Source/WebCore/dom/DOMCoreException.idl @@ -10,7 +10,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -27,8 +27,9 @@ */ [ - JSNoStaticTables, + Constructor(optional DOMString message = "", optional DOMString name = "Error"), DoNotCheckConstants, + Exposed=(Window,Worker), InterfaceName=DOMException, ImplementationLacksVTable, ] exception DOMCoreException { @@ -37,47 +38,32 @@ readonly attribute DOMString name; readonly attribute DOMString message; -#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT - // Override in a Mozilla compatible format [NotEnumerable] DOMString toString(); -#endif // ExceptionCode - const unsigned short INDEX_SIZE_ERR = 1; - const unsigned short DOMSTRING_SIZE_ERR = 2; - const unsigned short HIERARCHY_REQUEST_ERR = 3; - const unsigned short WRONG_DOCUMENT_ERR = 4; - const unsigned short INVALID_CHARACTER_ERR = 5; - const unsigned short NO_DATA_ALLOWED_ERR = 6; - const unsigned short NO_MODIFICATION_ALLOWED_ERR = 7; - const unsigned short NOT_FOUND_ERR = 8; - const unsigned short NOT_SUPPORTED_ERR = 9; - const unsigned short INUSE_ATTRIBUTE_ERR = 10; - // Introduced in DOM Level 2: - const unsigned short INVALID_STATE_ERR = 11; - // Introduced in DOM Level 2: - const unsigned short SYNTAX_ERR = 12; - // Introduced in DOM Level 2: - const unsigned short INVALID_MODIFICATION_ERR = 13; - // Introduced in DOM Level 2: - const unsigned short NAMESPACE_ERR = 14; - // Introduced in DOM Level 2: - const unsigned short INVALID_ACCESS_ERR = 15; - // Introduced in DOM Level 3: - const unsigned short VALIDATION_ERR = 16; - // Introduced in DOM Level 3: - const unsigned short TYPE_MISMATCH_ERR = 17; - // Introduced as an XHR extension: - const unsigned short SECURITY_ERR = 18; - // Introduced in HTML5: - const unsigned short NETWORK_ERR = 19; - const unsigned short ABORT_ERR = 20; - const unsigned short URL_MISMATCH_ERR = 21; - const unsigned short QUOTA_EXCEEDED_ERR = 22; - // TIMEOUT_ERR is currently unused but was added for completeness. - const unsigned short TIMEOUT_ERR = 23; - // INVALID_NODE_TYPE_ERR is currently unused but was added for completeness. - const unsigned short INVALID_NODE_TYPE_ERR = 24; - const unsigned short DATA_CLONE_ERR = 25; + const unsigned short INDEX_SIZE_ERR = 1; + const unsigned short DOMSTRING_SIZE_ERR = 2; + const unsigned short HIERARCHY_REQUEST_ERR = 3; + const unsigned short WRONG_DOCUMENT_ERR = 4; + const unsigned short INVALID_CHARACTER_ERR = 5; + const unsigned short NO_DATA_ALLOWED_ERR = 6; + const unsigned short NO_MODIFICATION_ALLOWED_ERR = 7; + const unsigned short NOT_FOUND_ERR = 8; + const unsigned short NOT_SUPPORTED_ERR = 9; + const unsigned short INUSE_ATTRIBUTE_ERR = 10; + const unsigned short INVALID_STATE_ERR = 11; + const unsigned short SYNTAX_ERR = 12; + const unsigned short INVALID_MODIFICATION_ERR = 13; + const unsigned short NAMESPACE_ERR = 14; + const unsigned short INVALID_ACCESS_ERR = 15; + const unsigned short VALIDATION_ERR = 16; + const unsigned short TYPE_MISMATCH_ERR = 17; + const unsigned short SECURITY_ERR = 18; + const unsigned short NETWORK_ERR = 19; + const unsigned short ABORT_ERR = 20; + const unsigned short URL_MISMATCH_ERR = 21; + const unsigned short QUOTA_EXCEEDED_ERR = 22; + const unsigned short TIMEOUT_ERR = 23; + const unsigned short INVALID_NODE_TYPE_ERR = 24; + const unsigned short DATA_CLONE_ERR = 25; }; - diff --git a/Source/WebCore/dom/DOMError.cpp b/Source/WebCore/dom/DOMError.cpp index 58f8afab6..bb146c16e 100644 --- a/Source/WebCore/dom/DOMError.cpp +++ b/Source/WebCore/dom/DOMError.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2012 Google Inc. All Rights Reserved. + * Copyright (C) 2016 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,11 +27,11 @@ #include "config.h" #include "DOMError.h" - namespace WebCore { -DOMError::DOMError(const String& name) +DOMError::DOMError(const String& name, const String& message) : m_name(name) + , m_message(message) { } diff --git a/Source/WebCore/dom/DOMError.h b/Source/WebCore/dom/DOMError.h index e9f3d0b4a..d2495177c 100644 --- a/Source/WebCore/dom/DOMError.h +++ b/Source/WebCore/dom/DOMError.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2012 Google Inc. All Rights Reserved. + * Copyright (C) 2016 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,10 +24,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DOMError_h -#define DOMError_h +#pragma once -#include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> #include <wtf/text/WTFString.h> @@ -34,21 +33,21 @@ namespace WebCore { class DOMError : public RefCounted<DOMError> { public: - static PassRefPtr<DOMError> create(const String& name) + static Ref<DOMError> create(const String& name, const String& message = { }) { - return adoptRef(new DOMError(name)); + return adoptRef(*new DOMError(name, message)); } virtual ~DOMError() { } const String& name() const { return m_name; } + const String& message() const { return m_message; } protected: - explicit DOMError(const String& name); + explicit DOMError(const String& name, const String& message); private: - const String m_name; + String m_name; + String m_message; }; } // namespace WebCore - -#endif // DOMError_h diff --git a/Source/WebCore/dom/DOMError.idl b/Source/WebCore/dom/DOMError.idl index 539dc7e6b..0d38a72af 100644 --- a/Source/WebCore/dom/DOMError.idl +++ b/Source/WebCore/dom/DOMError.idl @@ -10,7 +10,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -30,5 +30,6 @@ SkipVTableValidation ] interface DOMError { readonly attribute DOMString name; + readonly attribute DOMString message; }; diff --git a/Source/WebCore/dom/DOMExceptions.in b/Source/WebCore/dom/DOMExceptions.in index 6e824ff54..6148e8603 100644 --- a/Source/WebCore/dom/DOMExceptions.in +++ b/Source/WebCore/dom/DOMExceptions.in @@ -1,11 +1,8 @@ namespace=Exception DOMCoreException -EventException -FileException conditional=BLOB -RangeException -SQLException conditional=SQL_DATABASE -SVGException conditional=SVG -XMLHttpRequestException +FileException +SQLException +SVGException XPathException diff --git a/Source/WebCore/dom/DOMImplementation.cpp b/Source/WebCore/dom/DOMImplementation.cpp index 081c8560a..586fa1ccb 100644 --- a/Source/WebCore/dom/DOMImplementation.cpp +++ b/Source/WebCore/dom/DOMImplementation.cpp @@ -25,50 +25,46 @@ #include "config.h" #include "DOMImplementation.h" -#include "ContentType.h" #include "CSSStyleSheet.h" +#include "ContentType.h" #include "DocumentType.h" #include "Element.h" -#include "ExceptionCode.h" +#include "FTPDirectoryDocument.h" #include "Frame.h" #include "FrameLoader.h" #include "FrameLoaderClient.h" -#include "FTPDirectoryDocument.h" #include "HTMLDocument.h" -#include "HTMLViewSourceDocument.h" +#include "HTMLHeadElement.h" +#include "HTMLTitleElement.h" #include "Image.h" #include "ImageDocument.h" +#include "MIMETypeRegistry.h" +#include "MainFrame.h" #include "MediaDocument.h" #include "MediaList.h" -#include "MIMETypeRegistry.h" +#include "MediaPlayer.h" #include "Page.h" #include "PluginData.h" #include "PluginDocument.h" +#include "SVGDocument.h" +#include "SVGNames.h" #include "SecurityOrigin.h" +#include "SecurityOriginPolicy.h" #include "Settings.h" #include "StyleSheetContents.h" #include "SubframeLoader.h" +#include "Text.h" #include "TextDocument.h" -#include "XMLNames.h" +#include "XMLDocument.h" +#include <wtf/NeverDestroyed.h> #include <wtf/StdLibExtras.h> -#if ENABLE(SVG) -#include "SVGNames.h" -#include "SVGDocument.h" -#endif - namespace WebCore { -typedef HashSet<String, CaseFoldingHash> FeatureSet; - -#if ENABLE(SVG) -static void addString(FeatureSet& set, const char* string) -{ - set.add(string); -} -#endif +using namespace HTMLNames; #if ENABLE(VIDEO) + class DOMImplementationSupportsTypeClient : public MediaPlayerSupportsTypeClient { public: DOMImplementationSupportsTypeClient(bool needsHacks, const String& host) @@ -78,113 +74,13 @@ public: } private: - virtual bool mediaPlayerNeedsSiteSpecificHacks() const override { return m_needsHacks; } - virtual String mediaPlayerDocumentHost() const override { return m_host; } + bool mediaPlayerNeedsSiteSpecificHacks() const override { return m_needsHacks; } + String mediaPlayerDocumentHost() const override { return m_host; } bool m_needsHacks; String m_host; }; -#endif - -#if ENABLE(SVG) - -static bool isSupportedSVG10Feature(const String& feature, const String& version) -{ - if (!version.isEmpty() && version != "1.0") - return false; - - static bool initialized = false; - DEFINE_STATIC_LOCAL(FeatureSet, svgFeatures, ()); - if (!initialized) { -#if ENABLE(FILTERS) && ENABLE(SVG_FONTS) - addString(svgFeatures, "svg"); - addString(svgFeatures, "svg.static"); -#endif -// addString(svgFeatures, "svg.animation"); -// addString(svgFeatures, "svg.dynamic"); -// addString(svgFeatures, "svg.dom.animation"); -// addString(svgFeatures, "svg.dom.dynamic"); -#if ENABLE(FILTERS) && ENABLE(SVG_FONTS) - addString(svgFeatures, "dom"); - addString(svgFeatures, "dom.svg"); - addString(svgFeatures, "dom.svg.static"); -#endif -// addString(svgFeatures, "svg.all"); -// addString(svgFeatures, "dom.svg.all"); - initialized = true; - } - return feature.startsWith("org.w3c.", false) - && svgFeatures.contains(feature.right(feature.length() - 8)); -} - -static bool isSupportedSVG11Feature(const String& feature, const String& version) -{ - if (!version.isEmpty() && version != "1.1") - return false; - static bool initialized = false; - DEFINE_STATIC_LOCAL(FeatureSet, svgFeatures, ()); - if (!initialized) { - // Sadly, we cannot claim to implement any of the SVG 1.1 generic feature sets - // lack of Font and Filter support. - // http://bugs.webkit.org/show_bug.cgi?id=15480 -#if ENABLE(FILTERS) && ENABLE(SVG_FONTS) - addString(svgFeatures, "SVG"); - addString(svgFeatures, "SVGDOM"); - addString(svgFeatures, "SVG-static"); - addString(svgFeatures, "SVGDOM-static"); -#endif - addString(svgFeatures, "SVG-animation"); - addString(svgFeatures, "SVGDOM-animation"); -// addString(svgFeatures, "SVG-dynamic); -// addString(svgFeatures, "SVGDOM-dynamic); - addString(svgFeatures, "CoreAttribute"); - addString(svgFeatures, "Structure"); - addString(svgFeatures, "BasicStructure"); - addString(svgFeatures, "ContainerAttribute"); - addString(svgFeatures, "ConditionalProcessing"); - addString(svgFeatures, "Image"); - addString(svgFeatures, "Style"); - addString(svgFeatures, "ViewportAttribute"); - addString(svgFeatures, "Shape"); - addString(svgFeatures, "Text"); - addString(svgFeatures, "BasicText"); - addString(svgFeatures, "PaintAttribute"); - addString(svgFeatures, "BasicPaintAttribute"); - addString(svgFeatures, "OpacityAttribute"); - addString(svgFeatures, "GraphicsAttribute"); - addString(svgFeatures, "BaseGraphicsAttribute"); - addString(svgFeatures, "Marker"); -// addString(svgFeatures, "ColorProfile"); // requires color-profile, bug 6037 - addString(svgFeatures, "Gradient"); - addString(svgFeatures, "Pattern"); - addString(svgFeatures, "Clip"); - addString(svgFeatures, "BasicClip"); - addString(svgFeatures, "Mask"); -#if ENABLE(FILTERS) - addString(svgFeatures, "Filter"); - addString(svgFeatures, "BasicFilter"); -#endif - addString(svgFeatures, "DocumentEventsAttribute"); - addString(svgFeatures, "GraphicalEventsAttribute"); -// addString(svgFeatures, "AnimationEventsAttribute"); - addString(svgFeatures, "Cursor"); - addString(svgFeatures, "Hyperlinking"); - addString(svgFeatures, "XlinkAttribute"); - addString(svgFeatures, "ExternalResourcesRequired"); - addString(svgFeatures, "View"); - addString(svgFeatures, "Script"); - addString(svgFeatures, "Animation"); -#if ENABLE(SVG_FONTS) - addString(svgFeatures, "Font"); - addString(svgFeatures, "BasicFont"); -#endif - addString(svgFeatures, "Extensibility"); - initialized = true; - } - return feature.startsWith("http://www.w3.org/tr/svg11/feature#", false) - && svgFeatures.contains(feature.right(feature.length() - 35)); -} #endif DOMImplementation::DOMImplementation(Document& document) @@ -192,140 +88,81 @@ DOMImplementation::DOMImplementation(Document& document) { } -bool DOMImplementation::hasFeature(const String& feature, const String& version) -{ - if (feature.startsWith("http://www.w3.org/TR/SVG", false) - || feature.startsWith("org.w3c.dom.svg", false) - || feature.startsWith("org.w3c.svg", false)) { -#if ENABLE(SVG) - // FIXME: SVG 2.0 support? - return isSupportedSVG10Feature(feature, version) || isSupportedSVG11Feature(feature, version); -#else - UNUSED_PARAM(version); - return false; -#endif - } - - return true; -} - -PassRefPtr<DocumentType> DOMImplementation::createDocumentType(const String& qualifiedName, - const String& publicId, const String& systemId, ExceptionCode& ec) +ExceptionOr<Ref<DocumentType>> DOMImplementation::createDocumentType(const String& qualifiedName, const String& publicId, const String& systemId) { - String prefix, localName; - if (!Document::parseQualifiedName(qualifiedName, prefix, localName, ec)) - return 0; - + auto parseResult = Document::parseQualifiedName(qualifiedName); + if (parseResult.hasException()) + return parseResult.releaseException(); return DocumentType::create(m_document, qualifiedName, publicId, systemId); } -DOMImplementation* DOMImplementation::getInterface(const String& /*feature*/) +static inline Ref<XMLDocument> createXMLDocument(const String& namespaceURI) { - return 0; -} - -PassRefPtr<Document> DOMImplementation::createDocument(const String& namespaceURI, - const String& qualifiedName, DocumentType* doctype, ExceptionCode& ec) -{ - RefPtr<Document> doc; -#if ENABLE(SVG) if (namespaceURI == SVGNames::svgNamespaceURI) - doc = SVGDocument::create(0, URL()); - else -#endif + return SVGDocument::create(nullptr, URL()); if (namespaceURI == HTMLNames::xhtmlNamespaceURI) - doc = Document::createXHTML(0, URL()); - else - doc = Document::create(0, URL()); + return XMLDocument::createXHTML(nullptr, URL()); + return XMLDocument::create(nullptr, URL()); +} - doc->setSecurityOrigin(m_document.securityOrigin()); +ExceptionOr<Ref<XMLDocument>> DOMImplementation::createDocument(const String& namespaceURI, const String& qualifiedName, DocumentType* documentType) +{ + auto document = createXMLDocument(namespaceURI); + document->setContextDocument(m_document.contextDocument()); + document->setSecurityOriginPolicy(m_document.securityOriginPolicy()); - RefPtr<Node> documentElement; + RefPtr<Element> documentElement; if (!qualifiedName.isEmpty()) { - documentElement = doc->createElementNS(namespaceURI, qualifiedName, ec); - if (ec) - return 0; + ASSERT(!document->domWindow()); // If domWindow is not null, createElementNS could find CustomElementRegistry and arbitrary scripts. + auto result = document->createElementNS(namespaceURI, qualifiedName); + if (result.hasException()) + return result.releaseException(); + documentElement = result.releaseReturnValue(); } - if (doctype) - doc->appendChild(doctype); + if (documentType) + document->appendChild(*documentType); if (documentElement) - doc->appendChild(documentElement.release()); + document->appendChild(*documentElement); - return doc.release(); + return WTFMove(document); } -PassRefPtr<CSSStyleSheet> DOMImplementation::createCSSStyleSheet(const String&, const String& media, ExceptionCode&) +Ref<CSSStyleSheet> DOMImplementation::createCSSStyleSheet(const String&, const String& media) { // FIXME: Title should be set. // FIXME: Media could have wrong syntax, in which case we should generate an exception. auto sheet = CSSStyleSheet::create(StyleSheetContents::create()); - sheet.get().setMediaQueries(MediaQuerySet::createAllowingDescriptionSyntax(media)); - return std::move(sheet); -} - -static inline bool isValidXMLMIMETypeChar(UChar c) -{ - // Valid characters per RFCs 3023 and 2045: - // 0-9a-zA-Z_-+~!$^{}|.%'`#&* - return isASCIIAlphanumeric(c) || c == '!' || c == '#' || c == '$' || c == '%' || c == '&' || c == '\'' || c == '*' || c == '+' - || c == '-' || c == '.' || c == '^' || c == '_' || c == '`' || c == '{' || c == '|' || c == '}' || c == '~'; + sheet->setMediaQueries(MediaQuerySet::create(media)); + return sheet; } -bool DOMImplementation::isXMLMIMEType(const String& mimeType) +Ref<HTMLDocument> DOMImplementation::createHTMLDocument(const String& title) { - if (mimeType == "text/xml" || mimeType == "application/xml" || mimeType == "text/xsl") - return true; - - if (!mimeType.endsWith("+xml")) - return false; - - size_t slashPosition = mimeType.find('/'); - // Take into account the '+xml' ending of mimeType. - if (slashPosition == notFound || !slashPosition || slashPosition == mimeType.length() - 5) - return false; - - // Again, mimeType ends with '+xml', no need to check the validity of that substring. - for (size_t i = 0; i < mimeType.length() - 4; ++i) { - if (!isValidXMLMIMETypeChar(mimeType[i]) && i != slashPosition) - return false; + auto document = HTMLDocument::create(nullptr, URL()); + document->open(); + document->write("<!doctype html><html><head></head><body></body></html>"); + if (!title.isNull()) { + auto titleElement = HTMLTitleElement::create(titleTag, document); + titleElement->appendChild(document->createTextNode(title)); + ASSERT(document->head()); + document->head()->appendChild(titleElement); } - - return true; -} - -bool DOMImplementation::isTextMIMEType(const String& mimeType) -{ - if (MIMETypeRegistry::isSupportedJavaScriptMIMEType(mimeType) - || mimeType == "application/json" // Render JSON as text/plain. - || (mimeType.startsWith("text/") && mimeType != "text/html" - && mimeType != "text/xml" && mimeType != "text/xsl")) - return true; - - return false; -} - -PassRefPtr<HTMLDocument> DOMImplementation::createHTMLDocument(const String& title) -{ - RefPtr<HTMLDocument> d = HTMLDocument::create(0, URL()); - d->open(); - d->write("<!doctype html><html><body></body></html>"); - if (!title.isNull()) - d->setTitle(title); - d->setSecurityOrigin(m_document.securityOrigin()); - return d.release(); + document->setContextDocument(m_document.contextDocument()); + document->setSecurityOriginPolicy(m_document.securityOriginPolicy()); + return document; } -PassRefPtr<Document> DOMImplementation::createDocument(const String& type, Frame* frame, const URL& url, bool inViewSourceMode) +Ref<Document> DOMImplementation::createDocument(const String& type, Frame* frame, const URL& url) { - if (inViewSourceMode) - return HTMLViewSourceDocument::create(frame, url, type); + // FIXME: Confusing to have this here with public DOM APIs for creating documents. This is different enough that it should perhaps be moved. + // FIXME: This function is doing case insensitive comparisons on MIME types. Should do equalLettersIgnoringASCIICase instead. // Plugins cannot take HTML and XHTML from us, and we don't even need to initialize the plugin database for those. if (type == "text/html") return HTMLDocument::create(frame, url); if (type == "application/xhtml+xml") - return Document::createXHTML(frame, url); + return XMLDocument::createXHTML(frame, url); #if ENABLE(FTPDIR) // Plugins cannot take FTP from us either @@ -333,10 +170,14 @@ PassRefPtr<Document> DOMImplementation::createDocument(const String& type, Frame return FTPDirectoryDocument::create(frame, url); #endif - PluginData* pluginData = 0; - PluginData::AllowedPluginTypes allowedPluginTypes = PluginData::OnlyApplicationPlugins; + // If we want to useImageDocumentForSubframePDF, we'll let that override plugin support. + if (frame && !frame->isMainFrame() && MIMETypeRegistry::isPDFMIMEType(type) && frame->settings().useImageDocumentForSubframePDF()) + return ImageDocument::create(*frame, url); + + PluginData* pluginData = nullptr; + auto allowedPluginTypes = PluginData::OnlyApplicationPlugins; if (frame && frame->page()) { - if (frame->loader().subframeLoader().allowPlugins(NotAboutToInstantiatePlugin)) + if (frame->loader().subframeLoader().allowPlugins()) allowedPluginTypes = PluginData::AllPlugins; pluginData = &frame->page()->pluginData(); @@ -344,36 +185,35 @@ PassRefPtr<Document> DOMImplementation::createDocument(const String& type, Frame // PDF is one image type for which a plugin can override built-in support. // We do not want QuickTime to take over all image types, obviously. - if ((MIMETypeRegistry::isPDFOrPostScriptMIMEType(type)) && pluginData && pluginData->supportsMimeType(type, allowedPluginTypes)) + if (MIMETypeRegistry::isPDFOrPostScriptMIMEType(type) && pluginData && pluginData->supportsWebVisibleMimeType(type, allowedPluginTypes)) return PluginDocument::create(frame, url); if (Image::supportsType(type)) - return ImageDocument::create(frame, url); + return ImageDocument::create(*frame, url); -#if ENABLE(VIDEO) && !ENABLE(PLUGIN_PROXY_FOR_VIDEO) - // Check to see if the type can be played by our MediaPlayer, if so create a MediaDocument +#if ENABLE(VIDEO) + // Check to see if the type can be played by our MediaPlayer, if so create a MediaDocument // Key system is not applicable here. DOMImplementationSupportsTypeClient client(frame && frame->settings().needsSiteSpecificQuirks(), url.host()); MediaEngineSupportParameters parameters; parameters.type = type; parameters.url = url; if (MediaPlayer::supportsType(parameters, &client)) - return MediaDocument::create(frame, url); + return MediaDocument::create(frame, url); #endif // Everything else except text/plain can be overridden by plugins. In particular, Adobe SVG Viewer should be used for SVG, if installed. // Disallowing plug-ins to use text/plain prevents plug-ins from hijacking a fundamental type that the browser is expected to handle, // and also serves as an optimization to prevent loading the plug-in database in the common case. - if (type != "text/plain" && ((pluginData && pluginData->supportsMimeType(type, allowedPluginTypes)) || (frame && frame->loader().client().shouldAlwaysUsePluginDocument(type)))) + if (type != "text/plain" && ((pluginData && pluginData->supportsWebVisibleMimeType(type, allowedPluginTypes)) || (frame && frame->loader().client().shouldAlwaysUsePluginDocument(type)))) return PluginDocument::create(frame, url); - if (isTextMIMEType(type)) + if (MIMETypeRegistry::isTextMIMEType(type)) return TextDocument::create(frame, url); -#if ENABLE(SVG) if (type == "image/svg+xml") return SVGDocument::create(frame, url); -#endif - if (isXMLMIMEType(type)) - return Document::create(frame, url); + + if (MIMETypeRegistry::isXMLMIMEType(type)) + return XMLDocument::create(frame, url); return HTMLDocument::create(frame, url); } diff --git a/Source/WebCore/dom/DOMImplementation.h b/Source/WebCore/dom/DOMImplementation.h index 748057aa6..a40a2f691 100644 --- a/Source/WebCore/dom/DOMImplementation.h +++ b/Source/WebCore/dom/DOMImplementation.h @@ -2,7 +2,7 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2008, 2016 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -21,60 +21,32 @@ * */ -#ifndef DOMImplementation_h -#define DOMImplementation_h +#pragma once -#include "Document.h" -#include "MediaPlayer.h" -#include <wtf/Forward.h> -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> +#include "ExceptionOr.h" +#include "XMLDocument.h" namespace WebCore { -class CSSStyleSheet; -class Document; -class DocumentType; -class Frame; -class HTMLDocument; -class URL; - -typedef int ExceptionCode; - class DOMImplementation : public ScriptWrappable { WTF_MAKE_FAST_ALLOCATED; public: - static PassOwnPtr<DOMImplementation> create(Document& document) { return adoptPtr(new DOMImplementation(document)); } - + explicit DOMImplementation(Document&); + void ref() { m_document.ref(); } void deref() { m_document.deref(); } - Document* document() { return &m_document; } - - // DOM methods & attributes for DOMImplementation - static bool hasFeature(const String& feature, const String& version); - PassRefPtr<DocumentType> createDocumentType(const String& qualifiedName, const String& publicId, const String& systemId, ExceptionCode&); - PassRefPtr<Document> createDocument(const String& namespaceURI, const String& qualifiedName, DocumentType*, ExceptionCode&); - - DOMImplementation* getInterface(const String& feature); + Document& document() { return m_document; } - // From the DOMImplementationCSS interface - static PassRefPtr<CSSStyleSheet> createCSSStyleSheet(const String& title, const String& media, ExceptionCode&); + WEBCORE_EXPORT ExceptionOr<Ref<DocumentType>> createDocumentType(const String& qualifiedName, const String& publicId, const String& systemId); + WEBCORE_EXPORT ExceptionOr<Ref<XMLDocument>> createDocument(const String& namespaceURI, const String& qualifiedName, DocumentType*); + WEBCORE_EXPORT Ref<HTMLDocument> createHTMLDocument(const String& title); + static bool hasFeature() { return true; } + WEBCORE_EXPORT static Ref<CSSStyleSheet> createCSSStyleSheet(const String& title, const String& media); - // From the HTMLDOMImplementation interface - PassRefPtr<HTMLDocument> createHTMLDocument(const String& title); - - // Other methods (not part of DOM) - static PassRefPtr<Document> createDocument(const String& MIMEType, Frame*, const URL&, bool inViewSourceMode); - - static bool isXMLMIMEType(const String& MIMEType); - static bool isTextMIMEType(const String& MIMEType); + static Ref<Document> createDocument(const String& MIMEType, Frame*, const URL&); private: - explicit DOMImplementation(Document&); - Document& m_document; }; } // namespace WebCore - -#endif diff --git a/Source/WebCore/dom/DOMImplementation.idl b/Source/WebCore/dom/DOMImplementation.idl index 3d0a33d39..f5b40e917 100644 --- a/Source/WebCore/dom/DOMImplementation.idl +++ b/Source/WebCore/dom/DOMImplementation.idl @@ -19,31 +19,16 @@ */ [ + ExportToWrappedFunction, GenerateIsReachable=ImplDocument, ImplementationLacksVTable, ] interface DOMImplementation { + [NewObject, MayThrowException] DocumentType createDocumentType(DOMString qualifiedName, DOMString publicId, DOMString systemId); + [NewObject, MayThrowException] XMLDocument createDocument(DOMString? namespaceURI, [TreatNullAs=EmptyString] DOMString qualifiedName, optional DocumentType? doctype = null); + [NewObject] HTMLDocument createHTMLDocument(optional DOMString title); - // DOM Level 1 + boolean hasFeature(); - [ObjCLegacyUnnamedParameters] boolean hasFeature([Default=Undefined] optional DOMString feature, - [TreatNullAs=NullString, Default=Undefined] optional DOMString version); - - // DOM Level 2 - - [ObjCLegacyUnnamedParameters, RaisesException] DocumentType createDocumentType([TreatNullAs=NullString, TreatUndefinedAs=NullString, Default=Undefined] optional DOMString qualifiedName, - [TreatNullAs=NullString, TreatUndefinedAs=NullString, Default=Undefined] optional DOMString publicId, - [TreatNullAs=NullString, TreatUndefinedAs=NullString, Default=Undefined] optional DOMString systemId); - [ObjCLegacyUnnamedParameters, RaisesException] Document createDocument([TreatNullAs=NullString, Default=Undefined] optional DOMString namespaceURI, - [TreatNullAs=NullString, Default=Undefined] optional DOMString qualifiedName, - [TreatNullAs=NullString, Default=Undefined] optional DocumentType doctype); - - // DOMImplementationCSS interface from DOM Level 2 CSS - - [ObjCLegacyUnnamedParameters, RaisesException] CSSStyleSheet createCSSStyleSheet([Default=Undefined] optional DOMString title, - [Default=Undefined] optional DOMString media); - - // HTMLDOMImplementation interface from DOM Level 2 HTML - - HTMLDocument createHTMLDocument([Default=NullString] optional DOMString title); + // FIXME: Using "undefined" as default parameter value is wrong. + CSSStyleSheet createCSSStyleSheet(optional DOMString title = "undefined", optional DOMString media = "undefined"); }; - diff --git a/Source/WebCore/dom/DOMNamedFlowCollection.cpp b/Source/WebCore/dom/DOMNamedFlowCollection.cpp index 39b3784bf..cdc1f73fb 100644 --- a/Source/WebCore/dom/DOMNamedFlowCollection.cpp +++ b/Source/WebCore/dom/DOMNamedFlowCollection.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved. + * Copyright (C) 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,60 +27,67 @@ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ + #include "config.h" #include "DOMNamedFlowCollection.h" +#include "WebKitNamedFlow.h" +#include <wtf/text/AtomicStringHash.h> namespace WebCore { -DOMNamedFlowCollection::DOMNamedFlowCollection(const Vector<WebKitNamedFlow*>& namedFlows) +inline DOMNamedFlowCollection::DOMNamedFlowCollection(Vector<Ref<WebKitNamedFlow>>&& flows) + : m_flows(WTFMove(flows)) { - for (Vector<WebKitNamedFlow*>::const_iterator it = namedFlows.begin(); it != namedFlows.end(); ++it) - m_namedFlows.add(*it); } -unsigned long DOMNamedFlowCollection::length() const +Ref<DOMNamedFlowCollection> DOMNamedFlowCollection::create(Vector<Ref<WebKitNamedFlow>>&& flows) { - return m_namedFlows.size(); + return adoptRef(*new DOMNamedFlowCollection(WTFMove(flows))); } -PassRefPtr<WebKitNamedFlow> DOMNamedFlowCollection::item(unsigned long index) const +DOMNamedFlowCollection::~DOMNamedFlowCollection() { - if (index >= static_cast<unsigned long>(m_namedFlows.size())) - return 0; - DOMNamedFlowSet::const_iterator it = m_namedFlows.begin(); - for (unsigned long i = 0; i < index; ++i) - ++it; - return *it; } -PassRefPtr<WebKitNamedFlow> DOMNamedFlowCollection::namedItem(const AtomicString& name) const +WebKitNamedFlow* DOMNamedFlowCollection::item(unsigned index) const { - DOMNamedFlowSet::const_iterator it = m_namedFlows.find<String, DOMNamedFlowHashTranslator>(name); - if (it != m_namedFlows.end()) - return *it; - return 0; + if (index >= m_flows.size()) + return nullptr; + return const_cast<WebKitNamedFlow*>(m_flows[index].ptr()); } -bool DOMNamedFlowCollection::hasNamedItem(const AtomicString& name) const -{ - return namedItem(name); -} +struct DOMNamedFlowCollection::HashFunctions { + static unsigned hash(const WebKitNamedFlow* key) { return AtomicStringHash::hash(key->name()); } + static bool equal(const WebKitNamedFlow* a, const WebKitNamedFlow* b) { return a->name() == b->name(); } + static const bool safeToCompareToEmptyOrDeleted = false; -// The HashFunctions object used by the HashSet to compare between RefPtr<NamedFlows>. -// It is safe to set safeToCompareToEmptyOrDeleted because the HashSet will never contain null pointers or deleted values. -struct DOMNamedFlowCollection::DOMNamedFlowHashFunctions { - static unsigned hash(PassRefPtr<WebKitNamedFlow> key) { return DefaultHash<String>::Hash::hash(key->name()); } - static bool equal(PassRefPtr<WebKitNamedFlow> a, PassRefPtr<WebKitNamedFlow> b) { return a->name() == b->name(); } - static const bool safeToCompareToEmptyOrDeleted = true; + static unsigned hash(const AtomicString& key) { return AtomicStringHash::hash(key); } + static bool equal(const WebKitNamedFlow* a, const AtomicString& b) { return a->name() == b; } }; -// The HashTranslator is used to lookup a RefPtr<NamedFlow> in the set using a name. -struct DOMNamedFlowCollection::DOMNamedFlowHashTranslator { - static unsigned hash(const String& key) { return DefaultHash<String>::Hash::hash(key); } - static bool equal(PassRefPtr<WebKitNamedFlow> a, const String& b) { return a->name() == b; } -}; -} // namespace WebCore - +WebKitNamedFlow* DOMNamedFlowCollection::namedItem(const AtomicString& name) const +{ + if (m_flowsByName.isEmpty()) { + // No need to optimize the case where m_flows is empty; will do nothing very quickly. + for (auto& flow : m_flows) + m_flowsByName.add(const_cast<WebKitNamedFlow*>(flow.ptr())); + } + auto it = m_flowsByName.find<HashFunctions>(name); + if (it == m_flowsByName.end()) + return nullptr; + return *it; +} +const Vector<AtomicString>& DOMNamedFlowCollection::supportedPropertyNames() +{ + if (m_flowNames.isEmpty()) { + // No need to optimize the case where m_flows is empty; will do nothing relatively quickly. + m_flowNames.reserveInitialCapacity(m_flows.size()); + for (auto& flow : m_flows) + m_flowNames.uncheckedAppend(flow->name()); + } + return m_flowNames; +} +} // namespace WebCore diff --git a/Source/WebCore/dom/DOMNamedFlowCollection.h b/Source/WebCore/dom/DOMNamedFlowCollection.h index 62c8ca022..bbd74a099 100644 --- a/Source/WebCore/dom/DOMNamedFlowCollection.h +++ b/Source/WebCore/dom/DOMNamedFlowCollection.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved. + * Copyright (C) 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,42 +27,41 @@ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef DOMNamedFlowCollection_h -#define DOMNamedFlowCollection_h -#include "NamedFlowCollection.h" -#include <wtf/ListHashSet.h> -#include <wtf/PassRefPtr.h> +#pragma once + +#include <wtf/Forward.h> +#include <wtf/HashSet.h> #include <wtf/RefCounted.h> #include <wtf/Vector.h> namespace WebCore { -class Document; class WebKitNamedFlow; class DOMNamedFlowCollection : public RefCounted<DOMNamedFlowCollection> { public: - static PassRefPtr<DOMNamedFlowCollection> create(const Vector<WebKitNamedFlow*>& namedFlows) - { - return adoptRef(new DOMNamedFlowCollection(namedFlows)); - } - - unsigned long length() const; - PassRefPtr<WebKitNamedFlow> item(unsigned long index) const; - PassRefPtr<WebKitNamedFlow> namedItem(const AtomicString& name) const; + static Ref<DOMNamedFlowCollection> create(Vector<Ref<WebKitNamedFlow>>&&); + ~DOMNamedFlowCollection(); - bool hasNamedItem(const AtomicString& name) const; + unsigned length() const; + WebKitNamedFlow* item(unsigned index) const; + WebKitNamedFlow* namedItem(const AtomicString& name) const; + const Vector<AtomicString>& supportedPropertyNames(); private: - struct DOMNamedFlowHashFunctions; - struct DOMNamedFlowHashTranslator; + struct HashFunctions; + + explicit DOMNamedFlowCollection(Vector<Ref<WebKitNamedFlow>>&&); - typedef ListHashSet<RefPtr<WebKitNamedFlow>, 1, DOMNamedFlowHashFunctions> DOMNamedFlowSet; - explicit DOMNamedFlowCollection(const Vector<WebKitNamedFlow*>&); - DOMNamedFlowSet m_namedFlows; + const Vector<Ref<WebKitNamedFlow>> m_flows; + mutable HashSet<WebKitNamedFlow*, HashFunctions> m_flowsByName; + mutable Vector<AtomicString> m_flowNames; }; -} // namespace WebCore -#endif +inline unsigned DOMNamedFlowCollection::length() const +{ + return m_flows.size(); +} +} // namespace WebCore diff --git a/Source/WebCore/dom/DOMNamedFlowCollection.idl b/Source/WebCore/dom/DOMNamedFlowCollection.idl index 01a1ba2ae..ea3dae5a5 100644 --- a/Source/WebCore/dom/DOMNamedFlowCollection.idl +++ b/Source/WebCore/dom/DOMNamedFlowCollection.idl @@ -28,13 +28,14 @@ */ [ - NoInterfaceObject, Conditional=CSS_REGIONS, + ImplementationLacksVTable, InterfaceName=WebKitNamedFlowCollection, JSGenerateToJSObject, - ImplementationLacksVTable, + LegacyUnenumerableNamedProperties, + NoInterfaceObject ] interface DOMNamedFlowCollection { readonly attribute unsigned long length; - getter WebKitNamedFlow item(unsigned long index); - getter WebKitNamedFlow namedItem(DOMString name); + getter WebKitNamedFlow? item(unsigned long index); + getter WebKitNamedFlow? namedItem(DOMString name); }; diff --git a/Source/WebCore/dom/DOMPoint.h b/Source/WebCore/dom/DOMPoint.h new file mode 100644 index 000000000..b7cadb99a --- /dev/null +++ b/Source/WebCore/dom/DOMPoint.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2014 Adobe Systems Incorporated. All rights reserved. + * Copyright (C) 2016 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "DOMPointReadOnly.h" + +namespace WebCore { + +class DOMPoint final : public DOMPointReadOnly { + WTF_MAKE_FAST_ALLOCATED; +public: + static Ref<DOMPoint> create(double x, double y, double z, double w) { return adoptRef(*new DOMPoint(x, y, z, w)); } + static Ref<DOMPoint> create(const DOMPointInit& init) { return create(init.x, init.y, init.z, init.w); } + static Ref<DOMPoint> fromPoint(const DOMPointInit& init) { return create(init.x, init.y, init.z, init.w); } + + void setX(double x) { m_x = x; } + void setY(double y) { m_y = y; } + void setZ(double z) { m_z = z; } + void setW(double w) { m_w = w; } + +private: + DOMPoint(double x, double y, double z, double w) + : DOMPointReadOnly(x, y, z, w) + { + } +}; + +} // namespace WebCore diff --git a/Source/WebCore/dom/DOMPoint.idl b/Source/WebCore/dom/DOMPoint.idl new file mode 100644 index 000000000..7ae41ace5 --- /dev/null +++ b/Source/WebCore/dom/DOMPoint.idl @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2014 Adobe Systems Incorporated. All rights reserved. + * Copyright (C) 2016 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// The DOMPointInit constructor exists in https://www.w3.org/TR/geometry-1/ but is removed in https://drafts.fxtf.org/geometry/ +[ + Constructor(DOMPointInit point), + Constructor(optional unrestricted double x = 0, optional unrestricted double y = 0, + optional unrestricted double z = 0, optional unrestricted double w = 1), + Exposed=(Window,Worker), + ImplementationLacksVTable +] +interface DOMPoint : DOMPointReadOnly { + [NewObject] static DOMPoint fromPoint(optional DOMPointInit other); + + inherit attribute unrestricted double x; + inherit attribute unrestricted double y; + inherit attribute unrestricted double z; + inherit attribute unrestricted double w; +}; diff --git a/Source/WebCore/dom/DOMPointInit.h b/Source/WebCore/dom/DOMPointInit.h new file mode 100644 index 000000000..119e42d42 --- /dev/null +++ b/Source/WebCore/dom/DOMPointInit.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2014 Adobe Systems Incorporated. All rights reserved. + * Copyright (C) 2016 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +namespace WebCore { + +struct DOMPointInit { + double x { 0 }; + double y { 0 }; + double z { 0 }; + double w { 1 }; +}; + +} // namespace WebCore diff --git a/Source/WebCore/dom/DOMPointInit.idl b/Source/WebCore/dom/DOMPointInit.idl new file mode 100644 index 000000000..5f47f0701 --- /dev/null +++ b/Source/WebCore/dom/DOMPointInit.idl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2014 Adobe Systems Incorporated. All rights reserved. + * Copyright (C) 2016 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +dictionary DOMPointInit { + unrestricted double x = 0; + unrestricted double y = 0; + unrestricted double z = 0; + unrestricted double w = 1; +}; diff --git a/Source/WebCore/dom/DOMPointReadOnly.h b/Source/WebCore/dom/DOMPointReadOnly.h new file mode 100644 index 000000000..1b98302ac --- /dev/null +++ b/Source/WebCore/dom/DOMPointReadOnly.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2014 Adobe Systems Incorporated. All rights reserved. + * Copyright (C) 2016 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "DOMPointInit.h" +#include "ScriptWrappable.h" +#include <wtf/RefCounted.h> + +namespace WebCore { + +class DOMPointReadOnly : public ScriptWrappable, public RefCounted<DOMPointReadOnly> { + WTF_MAKE_FAST_ALLOCATED; +public: + static Ref<DOMPointReadOnly> create(double x, double y, double z, double w) { return adoptRef(*new DOMPointReadOnly(x, y, z, w)); } + static Ref<DOMPointReadOnly> create(const DOMPointInit& init) { return create(init.x, init.y, init.z, init.w); } + static Ref<DOMPointReadOnly> fromPoint(const DOMPointInit& init) { return create(init.x, init.y, init.z, init.w); } + + double x() const { return m_x; } + double y() const { return m_y; } + double z() const { return m_z; } + double w() const { return m_w; } + +protected: + DOMPointReadOnly(double x, double y, double z, double w) + : m_x(x) + , m_y(y) + , m_z(z) + , m_w(w) + { + } + + // Any of these can be NaN or Inf. + double m_x; + double m_y; + double m_z; + double m_w; +}; + +} // namespace WebCore + diff --git a/Source/WebCore/dom/DOMPointReadOnly.idl b/Source/WebCore/dom/DOMPointReadOnly.idl new file mode 100644 index 000000000..9a00b9f4f --- /dev/null +++ b/Source/WebCore/dom/DOMPointReadOnly.idl @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2014 Adobe Systems Incorporated. All rights reserved. + * Copyright (C) 2016 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// The DOMPointInit constructor exists in https://www.w3.org/TR/geometry-1/ but is removed in https://drafts.fxtf.org/geometry/ +[ + Constructor(DOMPointInit point), + Constructor(optional unrestricted double x = 0, optional unrestricted double y = 0, + optional unrestricted double z = 0, optional unrestricted double w = 1), + Exposed=(Window,Worker), + ImplementationLacksVTable +] +interface DOMPointReadOnly { + [NewObject] static DOMPointReadOnly fromPoint(optional DOMPointInit other); + + readonly attribute unrestricted double x; + readonly attribute unrestricted double y; + readonly attribute unrestricted double z; + readonly attribute unrestricted double w; + + // FIXME: No support for DOMMatrix yet (webkit.org/b/110001) + // DOMPoint matrixTransform(optional DOMMatrixInit matrix); + + serializer = { attribute }; +}; diff --git a/Source/WebCore/dom/DOMRect.h b/Source/WebCore/dom/DOMRect.h new file mode 100644 index 000000000..92307422f --- /dev/null +++ b/Source/WebCore/dom/DOMRect.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "DOMRectReadOnly.h" + +namespace WebCore { + +class DOMRect : public DOMRectReadOnly { + WTF_MAKE_FAST_ALLOCATED; +public: + static Ref<DOMRect> create(double x, double y, double width, double height) { return adoptRef(*new DOMRect(x, y, width, height)); } + static Ref<DOMRect> fromRect(const DOMRectInit& init) { return create(init.x, init.y, init.width, init.height); } + + void setX(double x) { m_x = x; } + void setY(double y) { m_y = y; } + + void setWidth(double width) { m_width = width; } + void setHeight(double height) { m_height = height; } + +private: + DOMRect(double x, double y, double width, double height) + : DOMRectReadOnly(x, y, width, height) + { + } +}; + +} diff --git a/Source/WebCore/dom/DOMRect.idl b/Source/WebCore/dom/DOMRect.idl new file mode 100644 index 000000000..47fef35db --- /dev/null +++ b/Source/WebCore/dom/DOMRect.idl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +// https://drafts.fxtf.org/geometry-1/#DOMRect + +[ + Constructor(optional unrestricted double x = 0, optional unrestricted double y = 0, + optional unrestricted double width = 0, optional unrestricted double height = 0), + Exposed=(Window,Worker), + ImplementationLacksVTable +] +interface DOMRect : DOMRectReadOnly { + [NewObject] static DOMRect fromRect(optional DOMRectInit other); + + inherit attribute unrestricted double x; + inherit attribute unrestricted double y; + inherit attribute unrestricted double width; + inherit attribute unrestricted double height; +}; diff --git a/Source/WebCore/dom/DOMRectInit.h b/Source/WebCore/dom/DOMRectInit.h new file mode 100644 index 000000000..65ca6fd91 --- /dev/null +++ b/Source/WebCore/dom/DOMRectInit.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +namespace WebCore { + +struct DOMRectInit { + double x { 0 }; + double y { 0 }; + double width { 0 }; + double height { 0 }; +}; + +} diff --git a/Source/WebCore/dom/DOMRectInit.idl b/Source/WebCore/dom/DOMRectInit.idl new file mode 100644 index 000000000..0a2fcc144 --- /dev/null +++ b/Source/WebCore/dom/DOMRectInit.idl @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +// https://drafts.fxtf.org/geometry-1/#DOMRect + +dictionary DOMRectInit { + unrestricted double x = 0; + unrestricted double y = 0; + unrestricted double width = 0; + unrestricted double height = 0; +}; diff --git a/Source/WebCore/dom/DOMRectReadOnly.h b/Source/WebCore/dom/DOMRectReadOnly.h new file mode 100644 index 000000000..06a5252fe --- /dev/null +++ b/Source/WebCore/dom/DOMRectReadOnly.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "DOMRectInit.h" +#include "ScriptWrappable.h" +#include <wtf/MathExtras.h> +#include <wtf/Ref.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class DOMRectReadOnly : public ScriptWrappable, public RefCounted<DOMRectReadOnly> { + WTF_MAKE_FAST_ALLOCATED; +public: + static Ref<DOMRectReadOnly> create(double x, double y, double width, double height) { return adoptRef(*new DOMRectReadOnly(x, y, width, height)); } + static Ref<DOMRectReadOnly> fromRect(const DOMRectInit& init) { return create(init.x, init.y, init.width, init.height); } + + double x() const { return m_x; } + double y() const { return m_y; } + double width() const { return m_width; } + double height() const { return m_height; } + + // Model NaN handling after Math.min, Math.max. + double top() const { return WTF::nanPropagatingMin(m_y, m_y + m_height); } + double right() const { return WTF::nanPropagatingMax(m_x, m_x + m_width); } + double bottom() const { return WTF::nanPropagatingMax(m_y, m_y + m_height); } + double left() const { return WTF::nanPropagatingMin(m_x, m_x + m_width); } + +protected: + DOMRectReadOnly(double x, double y, double width, double height) + : m_x(x) + , m_y(y) + , m_width(width) + , m_height(height) + { + } + + // Any of these can be NaN or Inf. + double m_x; + double m_y; + double m_width; // Can be negative. + double m_height; // Can be negative. +}; + +} diff --git a/Source/WebCore/dom/DOMRectReadOnly.idl b/Source/WebCore/dom/DOMRectReadOnly.idl new file mode 100644 index 000000000..06b7dce8a --- /dev/null +++ b/Source/WebCore/dom/DOMRectReadOnly.idl @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +// https://drafts.fxtf.org/geometry-1/#DOMRect + +[ + Constructor(optional unrestricted double x = 0, optional unrestricted double y = 0, + optional unrestricted double width = 0, optional unrestricted double height = 0), + Exposed=(Window,Worker), + ImplementationLacksVTable +] +interface DOMRectReadOnly { + [NewObject] static DOMRectReadOnly fromRect(optional DOMRectInit other); + + readonly attribute unrestricted double x; + readonly attribute unrestricted double y; + readonly attribute unrestricted double width; + readonly attribute unrestricted double height; + readonly attribute unrestricted double top; + readonly attribute unrestricted double right; + readonly attribute unrestricted double bottom; + readonly attribute unrestricted double left; + + serializer = { attribute }; +}; diff --git a/Source/WebCore/dom/DOMStringList.cpp b/Source/WebCore/dom/DOMStringList.cpp index bcc0d23ba..8826b9344 100644 --- a/Source/WebCore/dom/DOMStringList.cpp +++ b/Source/WebCore/dom/DOMStringList.cpp @@ -40,9 +40,8 @@ bool DOMStringList::contains(const String& string) const // FIXME: Currently, all consumers of DOMStringList store fairly small lists and thus an O(n) // algorithm is OK. But this may need to be optimized if larger amounts of data are // stored in m_strings. - size_t count = m_strings.size(); - for (size_t i = 0; i < count; ++i) { - if (m_strings[i] == string) + for (auto& value : m_strings) { + if (value == string) return true; } return false; diff --git a/Source/WebCore/dom/DOMStringList.h b/Source/WebCore/dom/DOMStringList.h index d365799bc..69cb2d7f9 100644 --- a/Source/WebCore/dom/DOMStringList.h +++ b/Source/WebCore/dom/DOMStringList.h @@ -23,10 +23,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DOMStringList_h -#define DOMStringList_h +#pragma once -#include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> #include <wtf/Vector.h> #include <wtf/text/WTFString.h> @@ -37,9 +35,9 @@ namespace WebCore { // than creating the list statically as is currently the only option. class DOMStringList : public RefCounted<DOMStringList> { public: - static PassRefPtr<DOMStringList> create() + static Ref<DOMStringList> create() { - return adoptRef(new DOMStringList()); + return adoptRef(*new DOMStringList); } bool isEmpty() const { return m_strings.isEmpty(); } @@ -61,6 +59,3 @@ private: }; } // namespace WebCore - -#endif // DOMStringList_h - diff --git a/Source/WebCore/dom/DOMStringList.idl b/Source/WebCore/dom/DOMStringList.idl index 9618e2437..b5e09e9cf 100644 --- a/Source/WebCore/dom/DOMStringList.idl +++ b/Source/WebCore/dom/DOMStringList.idl @@ -24,12 +24,11 @@ */ [ - JSCustomToNativeObject, - JSNoStaticTables, - ImplementationLacksVTable, + ImplementationLacksVTable ] interface DOMStringList { readonly attribute unsigned long length; - [TreatReturnedNullStringAs=Null] getter DOMString item([Default=Undefined] optional unsigned long index); - boolean contains([Default=Undefined] optional DOMString string); + getter DOMString? item(unsigned long index); + + boolean contains(DOMString string); }; diff --git a/Source/WebCore/dom/DOMStringMap.h b/Source/WebCore/dom/DOMStringMap.h index da1e41119..947e17bd3 100644 --- a/Source/WebCore/dom/DOMStringMap.h +++ b/Source/WebCore/dom/DOMStringMap.h @@ -23,15 +23,12 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DOMStringMap_h -#define DOMStringMap_h +#pragma once #include "DatasetDOMStringMap.h" namespace WebCore { -typedef DatasetDOMStringMap DOMStringMap; +using DOMStringMap = DatasetDOMStringMap; } // namespace WebCore - -#endif // DOMStringMap_h diff --git a/Source/WebCore/dom/DOMStringMap.idl b/Source/WebCore/dom/DOMStringMap.idl index 55db65d55..cbfa3fd5b 100644 --- a/Source/WebCore/dom/DOMStringMap.idl +++ b/Source/WebCore/dom/DOMStringMap.idl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010, 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,10 +26,14 @@ [ GenerateIsReachable=ImplElementRoot, CustomDeleteProperty, - CustomEnumerateProperty, CustomNamedSetter, + OverrideBuiltins, SkipVTableValidation, ] interface DOMStringMap { getter DOMString (DOMString name); + + // FIXME: Add support for the setter and deleter specials. + // [CEReactions] setter void (DOMString name, DOMString value); + // [CEReactions] deleter void (DOMString name); }; diff --git a/Source/WebCore/dom/DOMTimeStamp.h b/Source/WebCore/dom/DOMTimeStamp.h index ff615205c..ee21875a9 100644 --- a/Source/WebCore/dom/DOMTimeStamp.h +++ b/Source/WebCore/dom/DOMTimeStamp.h @@ -28,8 +28,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DOMTimeStamp_h -#define DOMTimeStamp_h +#pragma once namespace WebCore { @@ -46,5 +45,3 @@ inline double convertDOMTimeStampToSeconds(DOMTimeStamp milliseconds) } } // namespace WebCore - -#endif // DOMTimeStamp_h diff --git a/Source/WebCore/dom/Clipboard.cpp b/Source/WebCore/dom/DataTransfer.cpp index e40ec5a06..083d3967b 100644 --- a/Source/WebCore/dom/Clipboard.cpp +++ b/Source/WebCore/dom/DataTransfer.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,7 +24,7 @@ */ #include "config.h" -#include "Clipboard.h" +#include "DataTransfer.h" #include "CachedImage.h" #include "CachedImageClient.h" @@ -36,31 +36,31 @@ #include "HTMLImageElement.h" #include "Image.h" #include "Pasteboard.h" +#include "StaticPasteboard.h" namespace WebCore { #if ENABLE(DRAG_SUPPORT) class DragImageLoader final : private CachedImageClient { - WTF_MAKE_FAST_ALLOCATED; + WTF_MAKE_NONCOPYABLE(DragImageLoader); WTF_MAKE_FAST_ALLOCATED; public: - static PassOwnPtr<DragImageLoader> create(Clipboard*); + explicit DragImageLoader(DataTransfer*); void startLoading(CachedResourceHandle<CachedImage>&); void stopLoading(CachedResourceHandle<CachedImage>&); private: - DragImageLoader(Clipboard*); - virtual void imageChanged(CachedImage*, const IntRect*) override; - Clipboard* m_clipboard; + void imageChanged(CachedImage*, const IntRect*) override; + DataTransfer* m_dataTransfer; }; #endif -Clipboard::Clipboard(ClipboardAccessPolicy policy, PassOwnPtr<Pasteboard> pasteboard, ClipboardType type, bool forFileDrag) +DataTransfer::DataTransfer(DataTransferAccessPolicy policy, std::unique_ptr<Pasteboard> pasteboard, Type type, bool forFileDrag) : m_policy(policy) - , m_pasteboard(pasteboard) + , m_pasteboard(WTFMove(pasteboard)) #if ENABLE(DRAG_SUPPORT) - , m_forDrag(type != CopyAndPaste) + , m_forDrag(type == DragAndDrop) , m_forFileDrag(forFileDrag) , m_dropEffect(ASCIILiteral("uninitialized")) , m_effectAllowed(ASCIILiteral("uninitialized")) @@ -68,17 +68,17 @@ Clipboard::Clipboard(ClipboardAccessPolicy policy, PassOwnPtr<Pasteboard> pasteb #endif { #if !ENABLE(DRAG_SUPPORT) - ASSERT_UNUSED(type, type == CopyAndPaste); + ASSERT_UNUSED(type, type != DragAndDrop); ASSERT_UNUSED(forFileDrag, !forFileDrag); #endif } -PassRefPtr<Clipboard> Clipboard::createForCopyAndPaste(ClipboardAccessPolicy policy) +Ref<DataTransfer> DataTransfer::createForCopyAndPaste(DataTransferAccessPolicy policy) { - return adoptRef(new Clipboard(policy, policy == ClipboardWritable ? Pasteboard::createPrivate() : Pasteboard::createForCopyAndPaste())); + return adoptRef(*new DataTransfer(policy, policy == DataTransferAccessPolicy::Writable ? Pasteboard::createPrivate() : Pasteboard::createForCopyAndPaste())); } -Clipboard::~Clipboard() +DataTransfer::~DataTransfer() { #if ENABLE(DRAG_SUPPORT) if (m_dragImageLoader && m_dragImage) @@ -86,45 +86,40 @@ Clipboard::~Clipboard() #endif } -void Clipboard::setAccessPolicy(ClipboardAccessPolicy policy) +void DataTransfer::setAccessPolicy(DataTransferAccessPolicy policy) { - // Once the clipboard goes numb, it can never go back. - ASSERT(m_policy != ClipboardNumb || policy == ClipboardNumb); + // Once the dataTransfer goes numb, it can never go back. + ASSERT(m_policy != DataTransferAccessPolicy::Numb || policy == DataTransferAccessPolicy::Numb); m_policy = policy; } -bool Clipboard::canReadTypes() const +bool DataTransfer::canReadTypes() const { - return m_policy == ClipboardReadable || m_policy == ClipboardTypesReadable || m_policy == ClipboardWritable; + return m_policy == DataTransferAccessPolicy::Readable || m_policy == DataTransferAccessPolicy::TypesReadable || m_policy == DataTransferAccessPolicy::Writable; } -bool Clipboard::canReadData() const +bool DataTransfer::canReadData() const { - return m_policy == ClipboardReadable || m_policy == ClipboardWritable; + return m_policy == DataTransferAccessPolicy::Readable || m_policy == DataTransferAccessPolicy::Writable; } -bool Clipboard::canWriteData() const +bool DataTransfer::canWriteData() const { - return m_policy == ClipboardWritable; + return m_policy == DataTransferAccessPolicy::Writable; } -void Clipboard::clearData(const String& type) +void DataTransfer::clearData(const String& type) { if (!canWriteData()) return; - m_pasteboard->clear(type); -} - -void Clipboard::clearData() -{ - if (!canWriteData()) - return; - - m_pasteboard->clear(); + if (type.isNull()) + m_pasteboard->clear(); + else + m_pasteboard->clear(type); } -String Clipboard::getData(const String& type) const +String DataTransfer::getData(const String& type) const { if (!canReadData()) return String(); @@ -137,103 +132,132 @@ String Clipboard::getData(const String& type) const return m_pasteboard->readString(type); } -bool Clipboard::setData(const String& type, const String& data) +void DataTransfer::setData(const String& type, const String& data) { if (!canWriteData()) - return false; + return; #if ENABLE(DRAG_SUPPORT) if (m_forFileDrag) - return false; + return; #endif - return m_pasteboard->writeString(type, data); + m_pasteboard->writeString(type, data); } -Vector<String> Clipboard::types() const +Vector<String> DataTransfer::types() const { if (!canReadTypes()) - return Vector<String>(); + return { }; return m_pasteboard->types(); } -PassRefPtr<FileList> Clipboard::files() const +FileList& DataTransfer::files() const { - // FIXME: We could cache the computed file list if it was necessary and helpful. - // Currently, each access gets a new copy, and thus setData() modifications to the - // clipboard are not reflected in any FileList objects the page has accessed and stored. + bool newlyCreatedFileList = !m_fileList; + if (!m_fileList) + m_fileList = FileList::create(); - if (!canReadData()) - return FileList::create(); + if (!canReadData()) { + m_fileList->clear(); + return *m_fileList; + } #if ENABLE(DRAG_SUPPORT) - if (m_forDrag && !m_forFileDrag) - return FileList::create(); + if (m_forDrag && !m_forFileDrag) { + ASSERT(m_fileList->isEmpty()); + return *m_fileList; + } #endif - Vector<String> filenames = m_pasteboard->readFilenames(); - RefPtr<FileList> fileList = FileList::create(); - for (size_t i = 0; i < filenames.size(); ++i) - fileList->append(File::create(filenames[i], File::AllContentTypes)); - return fileList.release(); + if (newlyCreatedFileList) { + for (const String& filename : m_pasteboard->readFilenames()) + m_fileList->append(File::create(filename)); + } + return *m_fileList; +} + +bool DataTransfer::hasFileOfType(const String& type) +{ + ASSERT_WITH_SECURITY_IMPLICATION(canReadTypes()); + + for (const String& path : m_pasteboard->readFilenames()) { + if (equalIgnoringASCIICase(File::contentTypeForFile(path), type)) + return true; + } + + return false; +} + +bool DataTransfer::hasStringOfType(const String& type) +{ + ASSERT_WITH_SECURITY_IMPLICATION(canReadTypes()); + + return !type.isNull() && types().contains(type); +} + +Ref<DataTransfer> DataTransfer::createForInputEvent(const String& plainText, const String& htmlText) +{ + TypeToStringMap typeToStringMap; + typeToStringMap.set(ASCIILiteral("text/plain"), plainText); + typeToStringMap.set(ASCIILiteral("text/html"), htmlText); + return adoptRef(*new DataTransfer(DataTransferAccessPolicy::Readable, StaticPasteboard::create(WTFMove(typeToStringMap)), InputEvent)); } #if !ENABLE(DRAG_SUPPORT) -String Clipboard::dropEffect() const +String DataTransfer::dropEffect() const { return ASCIILiteral("none"); } -void Clipboard::setDropEffect(const String&) +void DataTransfer::setDropEffect(const String&) { } -String Clipboard::effectAllowed() const +String DataTransfer::effectAllowed() const { return ASCIILiteral("uninitialized"); } -void Clipboard::setEffectAllowed(const String&) +void DataTransfer::setEffectAllowed(const String&) { } -void Clipboard::setDragImage(Element*, int, int) +void DataTransfer::setDragImage(Element*, int, int) { } #else -PassRefPtr<Clipboard> Clipboard::createForDragAndDrop() +Ref<DataTransfer> DataTransfer::createForDrag() { - return adoptRef(new Clipboard(ClipboardWritable, Pasteboard::createForDragAndDrop(), DragAndDrop)); + return adoptRef(*new DataTransfer(DataTransferAccessPolicy::Writable, Pasteboard::createForDragAndDrop(), DragAndDrop)); } -PassRefPtr<Clipboard> Clipboard::createForDragAndDrop(ClipboardAccessPolicy policy, const DragData& dragData) +Ref<DataTransfer> DataTransfer::createForDrop(DataTransferAccessPolicy policy, const DragData& dragData) { - return adoptRef(new Clipboard(policy, Pasteboard::createForDragAndDrop(dragData), DragAndDrop, dragData.containsFiles())); + return adoptRef(*new DataTransfer(policy, Pasteboard::createForDragAndDrop(dragData), DragAndDrop, dragData.containsFiles())); } -bool Clipboard::canSetDragImage() const +bool DataTransfer::canSetDragImage() const { // Note that the spec doesn't actually allow drag image modification outside the dragstart // event. This capability is maintained for backwards compatiblity for ports that have // supported this in the past. On many ports, attempting to set a drag image outside the // dragstart operation is a no-op anyway. - return m_forDrag && (m_policy == ClipboardImageWritable || m_policy == ClipboardWritable); + return m_forDrag && (m_policy == DataTransferAccessPolicy::ImageWritable || m_policy == DataTransferAccessPolicy::Writable); } -void Clipboard::setDragImage(Element* element, int x, int y) +void DataTransfer::setDragImage(Element* element, int x, int y) { if (!canSetDragImage()) return; - CachedImage* image; - if (element && isHTMLImageElement(element) && !element->inDocument()) - image = toHTMLImageElement(element)->cachedImage(); - else - image = 0; + CachedImage* image = nullptr; + if (is<HTMLImageElement>(element) && !element->isConnected()) + image = downcast<HTMLImageElement>(*element).cachedImage(); m_dragLocation = IntPoint(x, y); @@ -242,16 +266,16 @@ void Clipboard::setDragImage(Element* element, int x, int y) m_dragImage = image; if (m_dragImage) { if (!m_dragImageLoader) - m_dragImageLoader = DragImageLoader::create(this); + m_dragImageLoader = std::make_unique<DragImageLoader>(this); m_dragImageLoader->startLoading(m_dragImage); } - m_dragImageElement = image ? 0 : element; + m_dragImageElement = image ? nullptr : element; updateDragImage(); } -void Clipboard::updateDragImage() +void DataTransfer::updateDragImage() { // Don't allow setting the image if we haven't started dragging yet; we'll rely on the dragging code // to install this drag image as part of getting the drag kicked off. @@ -259,16 +283,16 @@ void Clipboard::updateDragImage() return; IntPoint computedHotSpot; - DragImageRef computedImage = createDragImage(computedHotSpot); + auto computedImage = DragImage { createDragImage(computedHotSpot) }; if (!computedImage) return; - m_pasteboard->setDragImage(computedImage, computedHotSpot); + m_pasteboard->setDragImage(WTFMove(computedImage), computedHotSpot); } #if !PLATFORM(MAC) -DragImageRef Clipboard::createDragImage(IntPoint& location) const +DragImageRef DataTransfer::createDragImage(IntPoint& location) const { location = m_dragLocation; @@ -286,30 +310,25 @@ DragImageRef Clipboard::createDragImage(IntPoint& location) const #endif -PassOwnPtr<DragImageLoader> DragImageLoader::create(Clipboard* clipboard) -{ - return adoptPtr(new DragImageLoader(clipboard)); -} - -DragImageLoader::DragImageLoader(Clipboard* clipboard) - : m_clipboard(clipboard) +DragImageLoader::DragImageLoader(DataTransfer* dataTransfer) + : m_dataTransfer(dataTransfer) { } void DragImageLoader::startLoading(CachedResourceHandle<WebCore::CachedImage>& image) { // FIXME: Does this really trigger a load? Does it need to? - image->addClient(this); + image->addClient(*this); } void DragImageLoader::stopLoading(CachedResourceHandle<WebCore::CachedImage>& image) { - image->removeClient(this); + image->removeClient(*this); } void DragImageLoader::imageChanged(CachedImage*, const IntRect*) { - m_clipboard->updateDragImage(); + m_dataTransfer->updateDragImage(); } static DragOperation dragOpFromIEOp(const String& operation) @@ -356,38 +375,38 @@ static const char* IEOpFromDragOp(DragOperation operation) return "none"; } -DragOperation Clipboard::sourceOperation() const +DragOperation DataTransfer::sourceOperation() const { DragOperation operation = dragOpFromIEOp(m_effectAllowed); ASSERT(operation != DragOperationPrivate); return operation; } -DragOperation Clipboard::destinationOperation() const +DragOperation DataTransfer::destinationOperation() const { DragOperation operation = dragOpFromIEOp(m_dropEffect); ASSERT(operation == DragOperationCopy || operation == DragOperationNone || operation == DragOperationLink || operation == (DragOperation)(DragOperationGeneric | DragOperationMove) || operation == DragOperationEvery); return operation; } -void Clipboard::setSourceOperation(DragOperation operation) +void DataTransfer::setSourceOperation(DragOperation operation) { ASSERT_ARG(operation, operation != DragOperationPrivate); m_effectAllowed = IEOpFromDragOp(operation); } -void Clipboard::setDestinationOperation(DragOperation operation) +void DataTransfer::setDestinationOperation(DragOperation operation) { ASSERT_ARG(operation, operation == DragOperationCopy || operation == DragOperationNone || operation == DragOperationLink || operation == DragOperationGeneric || operation == DragOperationMove || operation == (DragOperation)(DragOperationGeneric | DragOperationMove)); m_dropEffect = IEOpFromDragOp(operation); } -String Clipboard::dropEffect() const +String DataTransfer::dropEffect() const { return m_dropEffect == "uninitialized" ? ASCIILiteral("none") : m_dropEffect; } -void Clipboard::setDropEffect(const String& effect) +void DataTransfer::setDropEffect(const String& effect) { if (!m_forDrag) return; @@ -403,12 +422,12 @@ void Clipboard::setDropEffect(const String& effect) m_dropEffect = effect; } -String Clipboard::effectAllowed() const +String DataTransfer::effectAllowed() const { return m_effectAllowed; } -void Clipboard::setEffectAllowed(const String& effect) +void DataTransfer::setEffectAllowed(const String& effect) { if (!m_forDrag) return; diff --git a/Source/WebCore/dom/DataTransfer.h b/Source/WebCore/dom/DataTransfer.h new file mode 100644 index 000000000..d24b9daaf --- /dev/null +++ b/Source/WebCore/dom/DataTransfer.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2003-2016 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#pragma once + +#include "CachedResourceHandle.h" +#include "DataTransferAccessPolicy.h" +#include "DragActions.h" +#include "DragImage.h" +#include "IntPoint.h" +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class CachedImage; +class DragData; +class DragImageLoader; +class Element; +class FileList; +class Pasteboard; + +class DataTransfer : public RefCounted<DataTransfer> { +public: + static Ref<DataTransfer> createForCopyAndPaste(DataTransferAccessPolicy); + static Ref<DataTransfer> createForInputEvent(const String& plainText, const String& htmlText); + + WEBCORE_EXPORT ~DataTransfer(); + + String dropEffect() const; + void setDropEffect(const String&); + + String effectAllowed() const; + void setEffectAllowed(const String&); + + Vector<String> types() const; + + FileList& files() const; + + void clearData(const String& type = String()); + + String getData(const String& type) const; + + void setData(const String& type, const String& data); + + void setDragImage(Element*, int x, int y); + + void setAccessPolicy(DataTransferAccessPolicy); + bool canReadTypes() const; + bool canReadData() const; + bool canWriteData() const; + + bool hasFileOfType(const String&); + bool hasStringOfType(const String&); + + Pasteboard& pasteboard() { return *m_pasteboard; } + +#if ENABLE(DRAG_SUPPORT) + static Ref<DataTransfer> createForDrag(); + static Ref<DataTransfer> createForDrop(DataTransferAccessPolicy, const DragData&); + + bool dropEffectIsUninitialized() const { return m_dropEffect == "uninitialized"; } + + DragOperation sourceOperation() const; + DragOperation destinationOperation() const; + void setSourceOperation(DragOperation); + void setDestinationOperation(DragOperation); + + void setDragHasStarted() { m_shouldUpdateDragImage = true; } + DragImageRef createDragImage(IntPoint& dragLocation) const; + void updateDragImage(); +#endif + +private: + enum Type { CopyAndPaste, DragAndDrop, InputEvent }; + DataTransfer(DataTransferAccessPolicy, std::unique_ptr<Pasteboard>, Type = CopyAndPaste, bool forFileDrag = false); + +#if ENABLE(DRAG_SUPPORT) + bool canSetDragImage() const; +#endif + + DataTransferAccessPolicy m_policy; + std::unique_ptr<Pasteboard> m_pasteboard; + + mutable RefPtr<FileList> m_fileList; + +#if ENABLE(DRAG_SUPPORT) + bool m_forDrag; + bool m_forFileDrag; + String m_dropEffect; + String m_effectAllowed; + bool m_shouldUpdateDragImage; + IntPoint m_dragLocation; + CachedResourceHandle<CachedImage> m_dragImage; + RefPtr<Element> m_dragImageElement; + std::unique_ptr<DragImageLoader> m_dragImageLoader; +#endif +}; + +} // namespace WebCore diff --git a/Source/WebCore/dom/Clipboard.idl b/Source/WebCore/dom/DataTransfer.idl index 0a6206153..fc415bb1b 100644 --- a/Source/WebCore/dom/Clipboard.idl +++ b/Source/WebCore/dom/DataTransfer.idl @@ -10,7 +10,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -28,17 +28,19 @@ [ SkipVTableValidation, -] interface Clipboard { +] interface DataTransfer { attribute DOMString dropEffect; attribute DOMString effectAllowed; - [CustomGetter] readonly attribute Array types; - readonly attribute FileList files; + // FIXME: items should use [SameObject] once that is supported. + [Conditional=DATA_TRANSFER_ITEMS] readonly attribute DataTransferItemList items; - void clearData(optional DOMString type); - DOMString getData(DOMString type); - boolean setData(DOMString type, DOMString data); - void setDragImage(Element image, long x, long y); + void setDragImage(Element? image, long x, long y); // FIXME: Element argument is not nullable in the HTML standard. - [Conditional=DATA_TRANSFER_ITEMS] readonly attribute DataTransferItemList items; + readonly attribute FrozenArray<DOMString> types; + DOMString getData(DOMString format); + void setData(DOMString format, DOMString data); + void clearData(optional DOMString format); + // FIXME: files should use [SameObject] once that is supported. + readonly attribute FileList files; }; diff --git a/Source/WebCore/dom/ClipboardAccessPolicy.h b/Source/WebCore/dom/DataTransferAccessPolicy.h index 7a54009e5..a50e87aa9 100644 --- a/Source/WebCore/dom/ClipboardAccessPolicy.h +++ b/Source/WebCore/dom/DataTransferAccessPolicy.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -22,16 +22,13 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -#ifndef ClipboardAccessPolicy_h -#define ClipboardAccessPolicy_h + +#pragma once namespace WebCore { -enum ClipboardAccessPolicy { - ClipboardNumb, ClipboardImageWritable, ClipboardWritable, ClipboardTypesReadable, ClipboardReadable +enum class DataTransferAccessPolicy { + Numb, ImageWritable, Writable, TypesReadable, Readable }; -} // namespace - -#endif +} // namespace WebCore diff --git a/Source/WebCore/dom/DataTransferItem.h b/Source/WebCore/dom/DataTransferItem.h index 11f58e979..d0e55e1d5 100644 --- a/Source/WebCore/dom/DataTransferItem.h +++ b/Source/WebCore/dom/DataTransferItem.h @@ -28,8 +28,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DataTransferItem_h -#define DataTransferItem_h +#pragma once #if ENABLE(DATA_TRANSFER_ITEMS) @@ -39,9 +38,7 @@ namespace WebCore { class Blob; -class File; class StringCallback; -class ScriptExecutionContext; class DataTransferItem : public RefCounted<DataTransferItem> { public: @@ -53,12 +50,10 @@ public: virtual String kind() const = 0; virtual String type() const = 0; - virtual void getAsString(PassRefPtr<StringCallback>) const = 0; - virtual PassRefPtr<Blob> getAsFile() const = 0; + virtual void getAsString(RefPtr<StringCallback>&&) const = 0; + virtual RefPtr<Blob> getAsFile() const = 0; }; } // namespace WebCore #endif // ENABLE(DATA_TRANSFER_ITEMS) - -#endif // DataTransferItem_h diff --git a/Source/WebCore/dom/DataTransferItem.idl b/Source/WebCore/dom/DataTransferItem.idl index 3027eaf9b..fb722b8c6 100644 --- a/Source/WebCore/dom/DataTransferItem.idl +++ b/Source/WebCore/dom/DataTransferItem.idl @@ -36,7 +36,7 @@ readonly attribute DOMString kind; readonly attribute DOMString type; - void getAsString([Default=Undefined] optional StringCallback callback); + void getAsString(optional StringCallback? callback); Blob getAsFile(); }; diff --git a/Source/WebCore/dom/DataTransferItemList.h b/Source/WebCore/dom/DataTransferItemList.h index 58db06370..172a9b36e 100644 --- a/Source/WebCore/dom/DataTransferItemList.h +++ b/Source/WebCore/dom/DataTransferItemList.h @@ -28,8 +28,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DataTransferItemList_h -#define DataTransferItemList_h +#pragma once #if ENABLE(DATA_TRANSFER_ITEMS) @@ -39,25 +38,21 @@ namespace WebCore { -class Clipboard; class File; -typedef int ExceptionCode; - +// FIXME: Unclear why this need to be an abstract base class. class DataTransferItemList : public RefCounted<DataTransferItemList> { public: virtual ~DataTransferItemList() { } - virtual size_t length() const = 0; - virtual PassRefPtr<DataTransferItem> item(unsigned long index) = 0; - virtual void deleteItem(unsigned long index, ExceptionCode&) = 0; + virtual unsigned length() const = 0; + virtual DataTransferItem* item(unsigned index) = 0; + virtual ExceptionOr<void> deleteItem(unsigned index) = 0; virtual void clear() = 0; - virtual void add(const String& data, const String& type, ExceptionCode&) = 0; - virtual void add(PassRefPtr<File>) = 0; + virtual ExceptionOr<void> add(const String& data, const String& type) = 0; + virtual void add(RefPtr<File>&&) = 0; }; } // namespace WebCore #endif // ENABLE(DATA_TRANSFER_ITEMS) - -#endif // DataTransferItemList_h diff --git a/Source/WebCore/dom/DataTransferItemList.idl b/Source/WebCore/dom/DataTransferItemList.idl index f60899310..bdf2a8700 100644 --- a/Source/WebCore/dom/DataTransferItemList.idl +++ b/Source/WebCore/dom/DataTransferItemList.idl @@ -35,11 +35,10 @@ ImplementationLacksVTable, ] interface DataTransferItemList { readonly attribute long length; - getter DataTransferItem item([Default=Undefined] optional unsigned long index); + getter DataTransferItem item(unsigned long index); void clear(); void add(File? file); - [RaisesException] void add([Default=Undefined] optional DOMString data, - [Default=Undefined] optional DOMString type); -}; + [MayThrowException] void add(optional DOMString data = "undefined", optional DOMString type = "undefined"); +}; diff --git a/Source/WebCore/dom/DatasetDOMStringMap.cpp b/Source/WebCore/dom/DatasetDOMStringMap.cpp index b4383dc2d..5f01ecf17 100644 --- a/Source/WebCore/dom/DatasetDOMStringMap.cpp +++ b/Source/WebCore/dom/DatasetDOMStringMap.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010, 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,6 +29,7 @@ #include "Element.h" #include "ExceptionCode.h" #include <wtf/ASCIICType.h> +#include <wtf/text/AtomicString.h> #include <wtf/text/StringBuilder.h> namespace WebCore { @@ -105,22 +106,39 @@ static bool isValidPropertyName(const String& name) return true; } -static String convertPropertyNameToAttributeName(const String& name) +template<typename CharacterType> +static inline AtomicString convertPropertyNameToAttributeName(const StringImpl& name) { - StringBuilder builder; - builder.append("data-"); + const CharacterType dataPrefix[] = { 'd', 'a', 't', 'a', '-' }; + + Vector<CharacterType, 32> buffer; unsigned length = name.length(); + buffer.reserveInitialCapacity(WTF_ARRAY_LENGTH(dataPrefix) + length); + + buffer.append(dataPrefix, WTF_ARRAY_LENGTH(dataPrefix)); + + const CharacterType* characters = name.characters<CharacterType>(); for (unsigned i = 0; i < length; ++i) { - UChar character = name[i]; + CharacterType character = characters[i]; if (isASCIIUpper(character)) { - builder.append('-'); - builder.append(toASCIILower(character)); + buffer.append('-'); + buffer.append(toASCIILower(character)); } else - builder.append(character); + buffer.append(character); } + return AtomicString(buffer.data(), buffer.size()); +} - return builder.toString(); +static AtomicString convertPropertyNameToAttributeName(const String& name) +{ + if (name.isNull()) + return nullAtom; + + StringImpl* nameImpl = name.impl(); + if (nameImpl->is8Bit()) + return convertPropertyNameToAttributeName<LChar>(*nameImpl); + return convertPropertyNameToAttributeName<UChar>(*nameImpl); } void DatasetDOMStringMap::ref() @@ -133,61 +151,58 @@ void DatasetDOMStringMap::deref() m_element.deref(); } -void DatasetDOMStringMap::getNames(Vector<String>& names) +Vector<String> DatasetDOMStringMap::supportedPropertyNames() const { - if (!m_element.hasAttributes()) - return; + Vector<String> names; - for (const Attribute& attribute : m_element.attributesIterator()) { - if (isValidAttributeName(attribute.localName())) - names.append(convertAttributeNameToPropertyName(attribute.localName())); + if (m_element.hasAttributes()) { + for (auto& attribute : m_element.attributesIterator()) { + if (isValidAttributeName(attribute.localName())) + names.append(convertAttributeNameToPropertyName(attribute.localName())); + } } + + return names; } -String DatasetDOMStringMap::item(const String& name) +std::optional<const AtomicString&> DatasetDOMStringMap::item(const String& propertyName) const { - if (!m_element.hasAttributes()) - return String(); - - for (const Attribute& attribute : m_element.attributesIterator()) { - if (propertyNameMatchesAttributeName(name, attribute.localName())) - return attribute.value(); + if (m_element.hasAttributes()) { + AttributeIteratorAccessor attributeIteratorAccessor = m_element.attributesIterator(); + + if (attributeIteratorAccessor.attributeCount() == 1) { + // If the node has a single attribute, it is the dataset member accessed in most cases. + // Building a new AtomicString in that case is overkill so we do a direct character comparison. + const Attribute& attribute = *attributeIteratorAccessor.begin(); + if (propertyNameMatchesAttributeName(propertyName, attribute.localName())) + return attribute.value(); + } else { + AtomicString attributeName = convertPropertyNameToAttributeName(propertyName); + for (const Attribute& attribute : attributeIteratorAccessor) { + if (attribute.localName() == attributeName) + return attribute.value(); + } + } } - return String(); + return std::nullopt; } -bool DatasetDOMStringMap::contains(const String& name) +String DatasetDOMStringMap::namedItem(const AtomicString& name) const { - if (!m_element.hasAttributes()) - return false; - - for (const Attribute& attribute : m_element.attributesIterator()) { - if (propertyNameMatchesAttributeName(name, attribute.localName())) - return true; - } - - return false; + return item(name).value_or(String { }); } -void DatasetDOMStringMap::setItem(const String& name, const String& value, ExceptionCode& ec) +ExceptionOr<void> DatasetDOMStringMap::setItem(const String& name, const String& value) { - if (!isValidPropertyName(name)) { - ec = SYNTAX_ERR; - return; - } - - m_element.setAttribute(convertPropertyNameToAttributeName(name), value, ec); + if (!isValidPropertyName(name)) + return Exception { SYNTAX_ERR }; + return m_element.setAttribute(convertPropertyNameToAttributeName(name), value); } -void DatasetDOMStringMap::deleteItem(const String& name, ExceptionCode& ec) +bool DatasetDOMStringMap::deleteItem(const String& name) { - if (!isValidPropertyName(name)) { - ec = SYNTAX_ERR; - return; - } - - m_element.removeAttribute(convertPropertyNameToAttributeName(name)); + return m_element.removeAttribute(convertPropertyNameToAttributeName(name)); } } // namespace WebCore diff --git a/Source/WebCore/dom/DatasetDOMStringMap.h b/Source/WebCore/dom/DatasetDOMStringMap.h index 3a4bfa843..a3914fd96 100644 --- a/Source/WebCore/dom/DatasetDOMStringMap.h +++ b/Source/WebCore/dom/DatasetDOMStringMap.h @@ -23,21 +23,17 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DatasetDOMStringMap_h -#define DatasetDOMStringMap_h +#pragma once +#include "ExceptionOr.h" #include "ScriptWrappable.h" -#include <wtf/Noncopyable.h> -#include <wtf/Vector.h> -#include <wtf/text/WTFString.h> namespace WebCore { class Element; -typedef int ExceptionCode; class DatasetDOMStringMap final : public ScriptWrappable { - WTF_MAKE_NONCOPYABLE(DatasetDOMStringMap); WTF_MAKE_FAST_ALLOCATED; + WTF_MAKE_FAST_ALLOCATED; public: explicit DatasetDOMStringMap(Element& element) : m_element(element) @@ -47,18 +43,18 @@ public: void ref(); void deref(); - void getNames(Vector<String>&); - String item(const String& name); - bool contains(const String& name); - void setItem(const String& name, const String& value, ExceptionCode&); - void deleteItem(const String& name, ExceptionCode&); + Vector<String> supportedPropertyNames() const; - Element* element() { return &m_element; } + String namedItem(const AtomicString& name) const; + ExceptionOr<void> setItem(const String& name, const String& value); + bool deleteItem(const String& name); + + Element& element() { return m_element; } private: + std::optional<const AtomicString&> item(const String& name) const; + Element& m_element; }; } // namespace WebCore - -#endif // DatasetDOMStringMap_h diff --git a/Source/WebCore/dom/DecodedDataDocumentParser.h b/Source/WebCore/dom/DecodedDataDocumentParser.h index 34934ecba..bbb06a3e8 100644 --- a/Source/WebCore/dom/DecodedDataDocumentParser.h +++ b/Source/WebCore/dom/DecodedDataDocumentParser.h @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DecodedDataDocumentParser_h -#define DecodedDataDocumentParser_h +#pragma once #include "DocumentParser.h" @@ -41,13 +40,11 @@ protected: private: // append is used by DocumentWriter::replaceDocument. - virtual void append(PassRefPtr<StringImpl>) = 0; + void append(RefPtr<StringImpl>&&) override = 0; // appendBytes and flush are used by DocumentWriter (the loader). - virtual void appendBytes(DocumentWriter&, const char* bytes, size_t length) override; - virtual void flush(DocumentWriter&) override; + void appendBytes(DocumentWriter&, const char* bytes, size_t length) override; + void flush(DocumentWriter&) override; }; -} - -#endif // DecodedDataDocumentParser_h +} // namespace WebCore diff --git a/Source/WebCore/dom/DeviceMotionClient.h b/Source/WebCore/dom/DeviceMotionClient.h index 7533f6c60..dbe9abee8 100644 --- a/Source/WebCore/dom/DeviceMotionClient.h +++ b/Source/WebCore/dom/DeviceMotionClient.h @@ -24,8 +24,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DeviceMotionClient_h -#define DeviceMotionClient_h +#pragma once #include "DeviceClient.h" #include <wtf/Noncopyable.h> @@ -49,5 +48,3 @@ public: void provideDeviceMotionTo(Page*, DeviceMotionClient*); } // namespace WebCore - -#endif // DeviceMotionClient_h diff --git a/Source/WebCore/dom/DeviceMotionController.cpp b/Source/WebCore/dom/DeviceMotionController.cpp index 89eb00262..1ca7aa72f 100644 --- a/Source/WebCore/dom/DeviceMotionController.cpp +++ b/Source/WebCore/dom/DeviceMotionController.cpp @@ -30,6 +30,7 @@ #include "DeviceMotionClient.h" #include "DeviceMotionData.h" #include "DeviceMotionEvent.h" +#include "EventNames.h" #include "Page.h" namespace WebCore { @@ -41,11 +42,6 @@ DeviceMotionController::DeviceMotionController(DeviceMotionClient* client) deviceMotionClient()->setController(this); } -PassOwnPtr<DeviceMotionController> DeviceMotionController::create(DeviceMotionClient* client) -{ - return adoptPtr(new DeviceMotionController(client)); -} - #if PLATFORM(IOS) // FIXME: We should look to reconcile the iOS and OpenSource differences with this class // so that we can either remove these methods or remove the PLATFORM(IOS)-guard. @@ -77,7 +73,7 @@ bool DeviceMotionController::hasLastData() return deviceMotionClient()->lastMotion(); } -PassRefPtr<Event> DeviceMotionController::getLastEvent() +RefPtr<Event> DeviceMotionController::getLastEvent() { return DeviceMotionEvent::create(eventNames().devicemotionEvent, deviceMotionClient()->lastMotion()); } @@ -101,7 +97,7 @@ bool DeviceMotionController::isActiveAt(Page* page) void provideDeviceMotionTo(Page* page, DeviceMotionClient* client) { - DeviceMotionController::provideTo(page, DeviceMotionController::supplementName(), DeviceMotionController::create(client)); + DeviceMotionController::provideTo(page, DeviceMotionController::supplementName(), std::make_unique<DeviceMotionController>(client)); } } // namespace WebCore diff --git a/Source/WebCore/dom/DeviceMotionController.h b/Source/WebCore/dom/DeviceMotionController.h index deb0620d1..8cd2e2946 100644 --- a/Source/WebCore/dom/DeviceMotionController.h +++ b/Source/WebCore/dom/DeviceMotionController.h @@ -24,8 +24,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DeviceMotionController_h -#define DeviceMotionController_h +#pragma once #include "DeviceController.h" #include <wtf/Noncopyable.h> @@ -35,12 +34,11 @@ namespace WebCore { class DeviceMotionClient; class DeviceMotionData; -class DeviceMotionController : public DeviceController { +class DeviceMotionController final : public DeviceController { WTF_MAKE_NONCOPYABLE(DeviceMotionController); public: - ~DeviceMotionController() { }; - - static PassOwnPtr<DeviceMotionController> create(DeviceMotionClient*); + explicit DeviceMotionController(DeviceMotionClient*); + virtual ~DeviceMotionController() { } #if PLATFORM(IOS) // FIXME: We should look to reconcile the iOS and OpenSource differences with this class @@ -52,17 +50,12 @@ public: void didChangeDeviceMotion(DeviceMotionData*); DeviceMotionClient* deviceMotionClient(); - virtual bool hasLastData() override; - virtual PassRefPtr<Event> getLastEvent() override; + bool hasLastData() override; + RefPtr<Event> getLastEvent() override; static const char* supplementName(); static DeviceMotionController* from(Page*); static bool isActiveAt(Page*); - -private: - explicit DeviceMotionController(DeviceMotionClient*); }; } // namespace WebCore - -#endif // DeviceMotionController_h diff --git a/Source/WebCore/dom/DeviceMotionData.cpp b/Source/WebCore/dom/DeviceMotionData.cpp index 441c80d16..06816a6c8 100644 --- a/Source/WebCore/dom/DeviceMotionData.cpp +++ b/Source/WebCore/dom/DeviceMotionData.cpp @@ -28,64 +28,20 @@ namespace WebCore { -PassRefPtr<DeviceMotionData::Acceleration> DeviceMotionData::Acceleration::create(bool canProvideX, double x, - bool canProvideY, double y, - bool canProvideZ, double z) +Ref<DeviceMotionData> DeviceMotionData::create() { - return adoptRef(new DeviceMotionData::Acceleration(canProvideX, x, canProvideY, y, canProvideZ, z)); + return adoptRef(*new DeviceMotionData); } -DeviceMotionData::Acceleration::Acceleration(bool canProvideX, double x, bool canProvideY, double y, bool canProvideZ, double z) - : m_x(x) - , m_y(y) - , m_z(z) - , m_canProvideX(canProvideX) - , m_canProvideY(canProvideY) - , m_canProvideZ(canProvideZ) - -{ -} - -PassRefPtr<DeviceMotionData::RotationRate> DeviceMotionData::RotationRate::create(bool canProvideAlpha, double alpha, - bool canProvideBeta, double beta, - bool canProvideGamma, double gamma) -{ - return adoptRef(new DeviceMotionData::RotationRate(canProvideAlpha, alpha, canProvideBeta, beta, canProvideGamma, gamma)); -} - -DeviceMotionData::RotationRate::RotationRate(bool canProvideAlpha, double alpha, bool canProvideBeta, double beta, bool canProvideGamma, double gamma) - : m_alpha(alpha) - , m_beta(beta) - , m_gamma(gamma) - , m_canProvideAlpha(canProvideAlpha) - , m_canProvideBeta(canProvideBeta) - , m_canProvideGamma(canProvideGamma) -{ -} - -PassRefPtr<DeviceMotionData> DeviceMotionData::create() -{ - return adoptRef(new DeviceMotionData); -} - -PassRefPtr<DeviceMotionData> DeviceMotionData::create(PassRefPtr<Acceleration> acceleration, PassRefPtr<Acceleration> accelerationIncludingGravity, - PassRefPtr<RotationRate> rotationRate, bool canProvideInterval, double interval) -{ - return adoptRef(new DeviceMotionData(acceleration, accelerationIncludingGravity, rotationRate, canProvideInterval, interval)); -} - -DeviceMotionData::DeviceMotionData() - : m_canProvideInterval(false) - , m_interval(0) +Ref<DeviceMotionData> DeviceMotionData::create(RefPtr<Acceleration>&& acceleration, RefPtr<Acceleration>&& accelerationIncludingGravity, RefPtr<RotationRate>&& rotationRate, std::optional<double> interval) { + return adoptRef(*new DeviceMotionData(WTFMove(acceleration), WTFMove(accelerationIncludingGravity), WTFMove(rotationRate), interval)); } -DeviceMotionData::DeviceMotionData(PassRefPtr<Acceleration> acceleration, PassRefPtr<Acceleration> accelerationIncludingGravity, - PassRefPtr<RotationRate> rotationRate, bool canProvideInterval, double interval) - : m_acceleration(acceleration) - , m_accelerationIncludingGravity(accelerationIncludingGravity) - , m_rotationRate(rotationRate) - , m_canProvideInterval(canProvideInterval) +DeviceMotionData::DeviceMotionData(RefPtr<Acceleration>&& acceleration, RefPtr<Acceleration>&& accelerationIncludingGravity, RefPtr<RotationRate>&& rotationRate, std::optional<double> interval) + : m_acceleration(WTFMove(acceleration)) + , m_accelerationIncludingGravity(WTFMove(accelerationIncludingGravity)) + , m_rotationRate(WTFMove(rotationRate)) , m_interval(interval) { } diff --git a/Source/WebCore/dom/DeviceMotionData.h b/Source/WebCore/dom/DeviceMotionData.h index 1d53b531f..ccdb87a08 100644 --- a/Source/WebCore/dom/DeviceMotionData.h +++ b/Source/WebCore/dom/DeviceMotionData.h @@ -23,10 +23,9 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DeviceMotionData_h -#define DeviceMotionData_h +#pragma once -#include <wtf/PassRefPtr.h> +#include <wtf/Optional.h> #include <wtf/RefCounted.h> #include <wtf/RefPtr.h> @@ -36,74 +35,79 @@ class DeviceMotionData : public RefCounted<DeviceMotionData> { public: class Acceleration : public RefCounted<DeviceMotionData::Acceleration> { public: - static PassRefPtr<Acceleration> create(bool canProvideX, double x, bool canProvideY, double y, bool canProvideZ, double z); - - bool canProvideX() const { return m_canProvideX; } - bool canProvideY() const { return m_canProvideY; } - bool canProvideZ() const { return m_canProvideZ; } - - double x() const { return m_x; } - double y() const { return m_y; } - double z() const { return m_z; } + static Ref<Acceleration> create() + { + return adoptRef(*new Acceleration); + } + static Ref<Acceleration> create(std::optional<double> x, std::optional<double> y, std::optional<double> z) + { + return adoptRef(*new Acceleration(x, y, z)); + } + + std::optional<double> x() const { return m_x; } + std::optional<double> y() const { return m_y; } + std::optional<double> z() const { return m_z; } private: - Acceleration(bool canProvideX, double x, bool canProvideY, double y, bool canProvideZ, double z); - - double m_x; - double m_y; - double m_z; - - bool m_canProvideX; - bool m_canProvideY; - bool m_canProvideZ; + Acceleration() = default; + Acceleration(std::optional<double> x, std::optional<double> y, std::optional<double> z) + : m_x(x) + , m_y(y) + , m_z(z) + { + } + + std::optional<double> m_x; + std::optional<double> m_y; + std::optional<double> m_z; }; class RotationRate : public RefCounted<DeviceMotionData::RotationRate> { public: - static PassRefPtr<RotationRate> create(bool canProvideAlpha, double alpha, bool canProvideBeta, double beta, bool canProvideGamma, double gamma); - - bool canProvideAlpha() const { return m_canProvideAlpha; } - bool canProvideBeta() const { return m_canProvideBeta; } - bool canProvideGamma() const { return m_canProvideGamma; } - - double alpha() const { return m_alpha; } - double beta() const { return m_beta; } - double gamma() const { return m_gamma; } + static Ref<RotationRate> create() + { + return adoptRef(*new RotationRate); + } + static Ref<RotationRate> create(std::optional<double> alpha, std::optional<double> beta, std::optional<double> gamma) + { + return adoptRef(*new RotationRate(alpha, beta, gamma)); + } + + std::optional<double> alpha() const { return m_alpha; } + std::optional<double> beta() const { return m_beta; } + std::optional<double> gamma() const { return m_gamma; } private: - RotationRate(bool canProvideAlpha, double alpha, bool canProvideBeta, double beta, bool canProvideGamma, double gamma); - - double m_alpha; - double m_beta; - double m_gamma; - - bool m_canProvideAlpha; - bool m_canProvideBeta; - bool m_canProvideGamma; + RotationRate() = default; + RotationRate(std::optional<double> alpha, std::optional<double> beta, std::optional<double> gamma) + : m_alpha(alpha) + , m_beta(beta) + , m_gamma(gamma) + { + } + + std::optional<double> m_alpha; + std::optional<double> m_beta; + std::optional<double> m_gamma; }; - static PassRefPtr<DeviceMotionData> create(); - static PassRefPtr<DeviceMotionData> create(PassRefPtr<Acceleration> acceleration, PassRefPtr<Acceleration> accelerationIncludingGravity, - PassRefPtr<RotationRate> rotationRate, bool canProvideInterval, double interval); + WEBCORE_EXPORT static Ref<DeviceMotionData> create(); + WEBCORE_EXPORT static Ref<DeviceMotionData> create(RefPtr<Acceleration>&&, RefPtr<Acceleration>&& accelerationIncludingGravity, RefPtr<RotationRate>&&, std::optional<double> interval); const Acceleration* acceleration() const { return m_acceleration.get(); } const Acceleration* accelerationIncludingGravity() const { return m_accelerationIncludingGravity.get(); } const RotationRate* rotationRate() const { return m_rotationRate.get(); } - double interval() const { return m_interval; } - bool canProvideInterval() const { return m_canProvideInterval; } + + std::optional<double> interval() const { return m_interval; } private: - DeviceMotionData(); - DeviceMotionData(PassRefPtr<Acceleration> acceleration, PassRefPtr<Acceleration> accelerationIncludingGravity, - PassRefPtr<RotationRate> rotationRate, bool canProvideInterval, double interval); + DeviceMotionData() = default; + DeviceMotionData(RefPtr<Acceleration>&&, RefPtr<Acceleration>&& accelerationIncludingGravity, RefPtr<RotationRate>&&, std::optional<double> interval); RefPtr<Acceleration> m_acceleration; RefPtr<Acceleration> m_accelerationIncludingGravity; RefPtr<RotationRate> m_rotationRate; - bool m_canProvideInterval; - double m_interval; + std::optional<double> m_interval; }; } // namespace WebCore - -#endif // DeviceMotionData_h diff --git a/Source/WebCore/dom/DeviceMotionEvent.cpp b/Source/WebCore/dom/DeviceMotionEvent.cpp index e0b49a8c3..d6c858ac8 100644 --- a/Source/WebCore/dom/DeviceMotionEvent.cpp +++ b/Source/WebCore/dom/DeviceMotionEvent.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -27,7 +27,6 @@ #include "DeviceMotionEvent.h" #include "DeviceMotionData.h" -#include "EventNames.h" namespace WebCore { @@ -46,13 +45,71 @@ DeviceMotionEvent::DeviceMotionEvent(const AtomicString& eventType, DeviceMotion { } -void DeviceMotionEvent::initDeviceMotionEvent(const AtomicString& type, bool bubbles, bool cancelable, DeviceMotionData* deviceMotionData) +static std::optional<DeviceMotionEvent::Acceleration> convert(const DeviceMotionData::Acceleration* acceleration) +{ + if (!acceleration) + return std::nullopt; + + return DeviceMotionEvent::Acceleration { acceleration->x(), acceleration->y(), acceleration->z() }; +} + +static std::optional<DeviceMotionEvent::RotationRate> convert(const DeviceMotionData::RotationRate* rotationRate) +{ + if (!rotationRate) + return std::nullopt; + + return DeviceMotionEvent::RotationRate { rotationRate->alpha(), rotationRate->beta(), rotationRate->gamma() }; +} + +static RefPtr<DeviceMotionData::Acceleration> convert(std::optional<DeviceMotionEvent::Acceleration>&& acceleration) +{ + if (!acceleration) + return nullptr; + + if (!acceleration->x && !acceleration->y && !acceleration->z) + return nullptr; + + return DeviceMotionData::Acceleration::create(acceleration->x, acceleration->y, acceleration->z); +} + +static RefPtr<DeviceMotionData::RotationRate> convert(std::optional<DeviceMotionEvent::RotationRate>&& rotationRate) +{ + if (!rotationRate) + return nullptr; + + if (!rotationRate->alpha && !rotationRate->beta && !rotationRate->gamma) + return nullptr; + + return DeviceMotionData::RotationRate::create(rotationRate->alpha, rotationRate->beta, rotationRate->gamma); +} + +std::optional<DeviceMotionEvent::Acceleration> DeviceMotionEvent::acceleration() const +{ + return convert(m_deviceMotionData->acceleration()); +} + +std::optional<DeviceMotionEvent::Acceleration> DeviceMotionEvent::accelerationIncludingGravity() const +{ + return convert(m_deviceMotionData->accelerationIncludingGravity()); +} + +std::optional<DeviceMotionEvent::RotationRate> DeviceMotionEvent::rotationRate() const +{ + return convert(m_deviceMotionData->rotationRate()); +} + +std::optional<double> DeviceMotionEvent::interval() const +{ + return m_deviceMotionData->interval(); +} + +void DeviceMotionEvent::initDeviceMotionEvent(const AtomicString& type, bool bubbles, bool cancelable, std::optional<DeviceMotionEvent::Acceleration>&& acceleration, std::optional<DeviceMotionEvent::Acceleration>&& accelerationIncludingGravity, std::optional<DeviceMotionEvent::RotationRate>&& rotationRate, std::optional<double> interval) { if (dispatched()) return; initEvent(type, bubbles, cancelable); - m_deviceMotionData = deviceMotionData; + m_deviceMotionData = DeviceMotionData::create(convert(WTFMove(acceleration)), convert(WTFMove(accelerationIncludingGravity)), convert(WTFMove(rotationRate)), interval); } EventInterface DeviceMotionEvent::eventInterface() const diff --git a/Source/WebCore/dom/DeviceMotionEvent.h b/Source/WebCore/dom/DeviceMotionEvent.h index 2f06d86b8..7a8e9a5a1 100644 --- a/Source/WebCore/dom/DeviceMotionEvent.h +++ b/Source/WebCore/dom/DeviceMotionEvent.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DeviceMotionEvent_h -#define DeviceMotionEvent_h +#pragma once #include "Event.h" @@ -32,31 +31,48 @@ namespace WebCore { class DeviceMotionData; -class DeviceMotionEvent : public Event { +class DeviceMotionEvent final : public Event { public: - ~DeviceMotionEvent(); - static PassRefPtr<DeviceMotionEvent> create() + virtual ~DeviceMotionEvent(); + + // FIXME: Merge this with DeviceMotionData::Acceleration + struct Acceleration { + std::optional<double> x; + std::optional<double> y; + std::optional<double> z; + }; + + // FIXME: Merge this with DeviceMotionData::RotationRate + struct RotationRate { + std::optional<double> alpha; + std::optional<double> beta; + std::optional<double> gamma; + }; + + static Ref<DeviceMotionEvent> create(const AtomicString& eventType, DeviceMotionData* deviceMotionData) { - return adoptRef(new DeviceMotionEvent); + return adoptRef(*new DeviceMotionEvent(eventType, deviceMotionData)); } - static PassRefPtr<DeviceMotionEvent> create(const AtomicString& eventType, DeviceMotionData* deviceMotionData) + + static Ref<DeviceMotionEvent> createForBindings() { - return adoptRef(new DeviceMotionEvent(eventType, deviceMotionData)); + return adoptRef(*new DeviceMotionEvent); } - void initDeviceMotionEvent(const AtomicString& type, bool bubbles, bool cancelable, DeviceMotionData*); + std::optional<Acceleration> acceleration() const; + std::optional<Acceleration> accelerationIncludingGravity() const; + std::optional<RotationRate> rotationRate() const; + std::optional<double> interval() const; - DeviceMotionData* deviceMotionData() const { return m_deviceMotionData.get(); } - - virtual EventInterface eventInterface() const override; + void initDeviceMotionEvent(const AtomicString& type, bool bubbles, bool cancelable, std::optional<Acceleration>&&, std::optional<Acceleration>&&, std::optional<RotationRate>&&, std::optional<double>); private: DeviceMotionEvent(); DeviceMotionEvent(const AtomicString& eventType, DeviceMotionData*); + EventInterface eventInterface() const override; + RefPtr<DeviceMotionData> m_deviceMotionData; }; } // namespace WebCore - -#endif // DeviceMotionEvent_h diff --git a/Source/WebCore/dom/DeviceMotionEvent.idl b/Source/WebCore/dom/DeviceMotionEvent.idl index 12e0df499..f2f5df20f 100644 --- a/Source/WebCore/dom/DeviceMotionEvent.idl +++ b/Source/WebCore/dom/DeviceMotionEvent.idl @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,18 +24,36 @@ */ [ - Conditional=DEVICE_ORIENTATION, + Conditional=DEVICE_ORIENTATION ] interface DeviceMotionEvent : Event { - [Custom] readonly attribute Acceleration acceleration; - [Custom] readonly attribute Acceleration accelerationIncludingGravity; - [Custom] readonly attribute RotationRate rotationRate; - [Custom] readonly attribute double interval; - [Custom] void initDeviceMotionEvent([Default=Undefined] optional DOMString type, - [Default=Undefined] optional boolean bubbles, - [Default=Undefined] optional boolean cancelable, - [Default=Undefined] optional Acceleration acceleration, - [Default=Undefined] optional Acceleration accelerationIncludingGravity, - [Default=Undefined] optional RotationRate rotationRate, - [Default=Undefined] optional double interval); + readonly attribute Acceleration? acceleration; + readonly attribute Acceleration? accelerationIncludingGravity; + readonly attribute RotationRate? rotationRate; + readonly attribute unrestricted double? interval; + + void initDeviceMotionEvent(optional DOMString type = "", + optional boolean bubbles = false, + optional boolean cancelable = false, + optional Acceleration? acceleration = null, + optional Acceleration? accelerationIncludingGravity = null, + optional RotationRate? rotationRate = null, + optional unrestricted double? interval = null); }; +[ + Conditional=DEVICE_ORIENTATION, + JSGenerateToJSObject +] dictionary Acceleration { + double? x; + double? y; + double? z; +}; + +[ + Conditional=DEVICE_ORIENTATION, + JSGenerateToJSObject +] dictionary RotationRate { + double? alpha; + double? beta; + double? gamma; +}; diff --git a/Source/WebCore/dom/DeviceOrientationClient.h b/Source/WebCore/dom/DeviceOrientationClient.h index 5b5d1768b..37763633b 100644 --- a/Source/WebCore/dom/DeviceOrientationClient.h +++ b/Source/WebCore/dom/DeviceOrientationClient.h @@ -24,10 +24,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DeviceOrientationClient_h -#define DeviceOrientationClient_h +#pragma once #include "DeviceClient.h" +#include "PlatformExportMacros.h" #include <wtf/Noncopyable.h> namespace WebCore { @@ -46,8 +46,6 @@ public: virtual void deviceOrientationControllerDestroyed() = 0; }; -void provideDeviceOrientationTo(Page*, DeviceOrientationClient*); +WEBCORE_EXPORT void provideDeviceOrientationTo(Page*, DeviceOrientationClient*); } // namespace WebCore - -#endif // DeviceOrientationClient_h diff --git a/Source/WebCore/dom/DeviceOrientationController.cpp b/Source/WebCore/dom/DeviceOrientationController.cpp index 5ce7c5dd2..072f71189 100644 --- a/Source/WebCore/dom/DeviceOrientationController.cpp +++ b/Source/WebCore/dom/DeviceOrientationController.cpp @@ -30,6 +30,7 @@ #include "DeviceOrientationClient.h" #include "DeviceOrientationData.h" #include "DeviceOrientationEvent.h" +#include "EventNames.h" #include "Page.h" namespace WebCore { @@ -49,11 +50,6 @@ DeviceOrientationController::DeviceOrientationController(DeviceOrientationClient #endif } -PassOwnPtr<DeviceOrientationController> DeviceOrientationController::create(DeviceOrientationClient* client) -{ - return adoptPtr(new DeviceOrientationController(client)); -} - void DeviceOrientationController::didChangeDeviceOrientation(DeviceOrientationData* orientation) { dispatchDeviceEvent(DeviceOrientationEvent::create(eventNames().deviceorientationEvent, orientation)); @@ -84,7 +80,7 @@ bool DeviceOrientationController::hasLastData() return deviceOrientationClient()->lastOrientation(); } -PassRefPtr<Event> DeviceOrientationController::getLastEvent() +RefPtr<Event> DeviceOrientationController::getLastEvent() { return DeviceOrientationEvent::create(eventNames().deviceorientationEvent, deviceOrientationClient()->lastOrientation()); } @@ -109,7 +105,7 @@ bool DeviceOrientationController::isActiveAt(Page* page) void provideDeviceOrientationTo(Page* page, DeviceOrientationClient* client) { - DeviceOrientationController::provideTo(page, DeviceOrientationController::supplementName(), DeviceOrientationController::create(client)); + DeviceOrientationController::provideTo(page, DeviceOrientationController::supplementName(), std::make_unique<DeviceOrientationController>(client)); } } // namespace WebCore diff --git a/Source/WebCore/dom/DeviceOrientationController.h b/Source/WebCore/dom/DeviceOrientationController.h index 56bae2d78..fb2c6bb58 100644 --- a/Source/WebCore/dom/DeviceOrientationController.h +++ b/Source/WebCore/dom/DeviceOrientationController.h @@ -24,11 +24,9 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DeviceOrientationController_h -#define DeviceOrientationController_h +#pragma once #include "DeviceController.h" -#include <wtf/HashCountedSet.h> #include <wtf/Noncopyable.h> namespace WebCore { @@ -37,12 +35,11 @@ class DeviceOrientationClient; class DeviceOrientationData; class Page; -class DeviceOrientationController : public DeviceController { +class DeviceOrientationController final : public DeviceController { WTF_MAKE_NONCOPYABLE(DeviceOrientationController); public: - ~DeviceOrientationController() { }; - - static PassOwnPtr<DeviceOrientationController> create(DeviceOrientationClient*); + explicit DeviceOrientationController(DeviceOrientationClient*); + virtual ~DeviceOrientationController() { } void didChangeDeviceOrientation(DeviceOrientationData*); DeviceOrientationClient* deviceOrientationClient(); @@ -53,18 +50,13 @@ public: void suspendUpdates(); void resumeUpdates(); #else - virtual bool hasLastData() override; - virtual PassRefPtr<Event> getLastEvent() override; + bool hasLastData() override; + RefPtr<Event> getLastEvent() override; #endif static const char* supplementName(); static DeviceOrientationController* from(Page*); static bool isActiveAt(Page*); - -private: - DeviceOrientationController(DeviceOrientationClient*); }; } // namespace WebCore - -#endif // DeviceOrientationController_h diff --git a/Source/WebCore/dom/DeviceOrientationData.cpp b/Source/WebCore/dom/DeviceOrientationData.cpp index d6a5f2cf4..885912600 100644 --- a/Source/WebCore/dom/DeviceOrientationData.cpp +++ b/Source/WebCore/dom/DeviceOrientationData.cpp @@ -28,134 +28,38 @@ namespace WebCore { -PassRefPtr<DeviceOrientationData> DeviceOrientationData::create() -{ - return adoptRef(new DeviceOrientationData); -} - #if PLATFORM(IOS) -// FIXME: We should reconcile the iOS and OpenSource differences. -PassRefPtr<DeviceOrientationData> DeviceOrientationData::create(bool canProvideAlpha, double alpha, bool canProvideBeta, double beta, bool canProvideGamma, double gamma, bool canProvideCompassHeading, double compassHeading, bool canProvideCompassAccuracy, double compassAccuracy) -{ - return adoptRef(new DeviceOrientationData(canProvideAlpha, alpha, canProvideBeta, beta, canProvideGamma, gamma, canProvideCompassHeading, compassHeading, canProvideCompassAccuracy, compassAccuracy)); -} -#else -PassRefPtr<DeviceOrientationData> DeviceOrientationData::create(bool canProvideAlpha, double alpha, bool canProvideBeta, double beta, bool canProvideGamma, double gamma, bool canProvideAbsolute, bool absolute) -{ - return adoptRef(new DeviceOrientationData(canProvideAlpha, alpha, canProvideBeta, beta, canProvideGamma, gamma, canProvideAbsolute, absolute)); -} -#endif - -DeviceOrientationData::DeviceOrientationData() - : m_canProvideAlpha(false) - , m_canProvideBeta(false) - , m_canProvideGamma(false) -#if !PLATFORM(IOS) - , m_canProvideAbsolute(false) -#endif - , m_alpha(0) - , m_beta(0) - , m_gamma(0) -#if PLATFORM(IOS) - , m_canProvideCompassHeading(false) - , m_canProvideCompassAccuracy(false) -#else - , m_absolute(false) -#endif +// FIXME: We should reconcile the iOS and OpenSource differences. +Ref<DeviceOrientationData> DeviceOrientationData::create(std::optional<double> alpha, std::optional<double> beta, std::optional<double> gamma, std::optional<double> compassHeading, std::optional<double> compassAccuracy) { + return adoptRef(*new DeviceOrientationData(alpha, beta, gamma, compassHeading, compassAccuracy)); } -#if PLATFORM(IOS) -DeviceOrientationData::DeviceOrientationData(bool canProvideAlpha, double alpha, bool canProvideBeta, double beta, bool canProvideGamma, double gamma, bool canProvideCompassHeading, double compassHeading, bool canProvideCompassAccuracy, double compassAccuracy) - : m_canProvideAlpha(canProvideAlpha) - , m_canProvideBeta(canProvideBeta) - , m_canProvideGamma(canProvideGamma) - , m_alpha(alpha) +DeviceOrientationData::DeviceOrientationData(std::optional<double> alpha, std::optional<double> beta, std::optional<double> gamma, std::optional<double> compassHeading, std::optional<double> compassAccuracy) + : m_alpha(alpha) , m_beta(beta) , m_gamma(gamma) - , m_canProvideCompassHeading(canProvideCompassHeading) - , m_canProvideCompassAccuracy(canProvideCompassAccuracy) , m_compassHeading(compassHeading) , m_compassAccuracy(compassAccuracy) -#else -DeviceOrientationData::DeviceOrientationData(bool canProvideAlpha, double alpha, bool canProvideBeta, double beta, bool canProvideGamma, double gamma, bool canProvideAbsolute, bool absolute) - : m_canProvideAlpha(canProvideAlpha) - , m_canProvideBeta(canProvideBeta) - , m_canProvideGamma(canProvideGamma) - , m_canProvideAbsolute(canProvideAbsolute) - , m_alpha(alpha) - , m_beta(beta) - , m_gamma(gamma) - , m_absolute(absolute) -#endif { } -double DeviceOrientationData::alpha() const -{ - return m_alpha; -} - -double DeviceOrientationData::beta() const -{ - return m_beta; -} - -double DeviceOrientationData::gamma() const -{ - return m_gamma; -} - -#if !PLATFORM(IOS) -bool DeviceOrientationData::absolute() const -{ - return m_absolute; -} -#endif - -bool DeviceOrientationData::canProvideAlpha() const -{ - return m_canProvideAlpha; -} - -bool DeviceOrientationData::canProvideBeta() const -{ - return m_canProvideBeta; -} - -bool DeviceOrientationData::canProvideGamma() const -{ - return m_canProvideGamma; -} - -#if PLATFORM(IOS) -double DeviceOrientationData::compassHeading() const -{ - return m_compassHeading; -} - -double DeviceOrientationData::compassAccuracy() const -{ - return m_compassAccuracy; -} +#else -bool DeviceOrientationData::canProvideCompassHeading() const +Ref<DeviceOrientationData> DeviceOrientationData::create(std::optional<double> alpha, std::optional<double> beta, std::optional<double> gamma, std::optional<bool> absolute) { - return m_canProvideCompassHeading; + return adoptRef(*new DeviceOrientationData(alpha, beta, gamma, absolute)); } -bool DeviceOrientationData::canProvideCompassAccuracy() const +DeviceOrientationData::DeviceOrientationData(std::optional<double> alpha, std::optional<double> beta, std::optional<double> gamma, std::optional<bool> absolute) + : m_alpha(alpha) + , m_beta(beta) + , m_gamma(gamma) + , m_absolute(absolute) { - return m_canProvideCompassAccuracy; } -#endif -#if !PLATFORM(IOS) -bool DeviceOrientationData::canProvideAbsolute() const -{ - return m_canProvideAbsolute; -} #endif } // namespace WebCore diff --git a/Source/WebCore/dom/DeviceOrientationData.h b/Source/WebCore/dom/DeviceOrientationData.h index facf91709..818faddd6 100644 --- a/Source/WebCore/dom/DeviceOrientationData.h +++ b/Source/WebCore/dom/DeviceOrientationData.h @@ -23,67 +23,55 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DeviceOrientationData_h -#define DeviceOrientationData_h +#pragma once -#include <wtf/PassRefPtr.h> +#include "PlatformExportMacros.h" +#include <wtf/Optional.h> +#include <wtf/Ref.h> #include <wtf/RefCounted.h> namespace WebCore { class DeviceOrientationData : public RefCounted<DeviceOrientationData> { public: - static PassRefPtr<DeviceOrientationData> create(); + static Ref<DeviceOrientationData> create() + { + return adoptRef(*new DeviceOrientationData); + } + #if PLATFORM(IOS) - static PassRefPtr<DeviceOrientationData> create(bool canProvideAlpha, double alpha, bool canProvideBeta, double beta, bool canProvideGamma, double gamma, bool canProvideCompassHeading, double compassHeading, bool canProvideCompassAccuracy, double compassAccuracy); + WEBCORE_EXPORT static Ref<DeviceOrientationData> create(std::optional<double> alpha, std::optional<double> beta, std::optional<double> gamma, std::optional<double> compassHeading, std::optional<double> compassAccuracy); #else - static PassRefPtr<DeviceOrientationData> create(bool canProvideAlpha, double alpha, bool canProvideBeta, double beta, bool canProvideGamma, double gamma, bool canProvideAbsolute = false, bool absolute = false); + WEBCORE_EXPORT static Ref<DeviceOrientationData> create(std::optional<double> alpha, std::optional<double> beta, std::optional<double> gamma, std::optional<bool> absolute); #endif - double alpha() const; - double beta() const; - double gamma() const; - bool absolute() const; - bool canProvideAlpha() const; - bool canProvideBeta() const; - bool canProvideGamma() const; - bool canProvideAbsolute() const; - + std::optional<double> alpha() const { return m_alpha; } + std::optional<double> beta() const { return m_beta; } + std::optional<double> gamma() const { return m_gamma; } #if PLATFORM(IOS) - double compassHeading() const; - double compassAccuracy() const; - bool canProvideCompassHeading() const; - bool canProvideCompassAccuracy() const; + std::optional<double> compassHeading() const { return m_compassHeading; } + std::optional<double> compassAccuracy() const { return m_compassAccuracy; } +#else + std::optional<bool> absolute() const { return m_absolute; } #endif private: - DeviceOrientationData(); + DeviceOrientationData() = default; #if PLATFORM(IOS) - DeviceOrientationData(bool canProvideAlpha, double alpha, bool canProvideBeta, double beta, bool canProvideGamma, double gamma, bool canProvideCompassHeading, double compassHeading, bool canProvideCompassAccuracy, double compassAccuracy); + DeviceOrientationData(std::optional<double> alpha, std::optional<double> beta, std::optional<double> gamma, std::optional<double> compassHeading, std::optional<double> compassAccuracy); #else - DeviceOrientationData(bool canProvideAlpha, double alpha, bool canProvideBeta, double beta, bool canProvideGamma, double gamma, bool canProvideAbsolute, bool absolute); + DeviceOrientationData(std::optional<double> alpha, std::optional<double> beta, std::optional<double> gamma, std::optional<bool> absolute); #endif - bool m_canProvideAlpha; - bool m_canProvideBeta; - bool m_canProvideGamma; -#if !PLATFORM(IOS) - bool m_canProvideAbsolute; -#endif - double m_alpha; - double m_beta; - double m_gamma; - + std::optional<double> m_alpha; + std::optional<double> m_beta; + std::optional<double> m_gamma; #if PLATFORM(IOS) - bool m_canProvideCompassHeading; - bool m_canProvideCompassAccuracy; - double m_compassHeading; - double m_compassAccuracy; + std::optional<double> m_compassHeading; + std::optional<double> m_compassAccuracy; #else - bool m_absolute; + std::optional<bool> m_absolute; #endif }; } // namespace WebCore - -#endif // DeviceOrientationData_h diff --git a/Source/WebCore/dom/DeviceOrientationEvent.cpp b/Source/WebCore/dom/DeviceOrientationEvent.cpp index 97b919e59..e06ec3170 100644 --- a/Source/WebCore/dom/DeviceOrientationEvent.cpp +++ b/Source/WebCore/dom/DeviceOrientationEvent.cpp @@ -27,7 +27,6 @@ #include "DeviceOrientationEvent.h" #include "DeviceOrientationData.h" -#include "EventNames.h" namespace WebCore { @@ -46,15 +45,60 @@ DeviceOrientationEvent::DeviceOrientationEvent(const AtomicString& eventType, De { } -void DeviceOrientationEvent::initDeviceOrientationEvent(const AtomicString& type, bool bubbles, bool cancelable, DeviceOrientationData* orientation) +std::optional<double> DeviceOrientationEvent::alpha() const +{ + return m_orientation->alpha(); +} + +std::optional<double> DeviceOrientationEvent::beta() const +{ + return m_orientation->beta(); +} + +std::optional<double> DeviceOrientationEvent::gamma() const +{ + return m_orientation->gamma(); +} + +#if PLATFORM(IOS) + +std::optional<double> DeviceOrientationEvent::compassHeading() const +{ + return m_orientation->compassHeading(); +} + +std::optional<double> DeviceOrientationEvent::compassAccuracy() const +{ + return m_orientation->compassAccuracy(); +} + +void DeviceOrientationEvent::initDeviceOrientationEvent(const AtomicString& type, bool bubbles, bool cancelable, std::optional<double> alpha, std::optional<double> beta, std::optional<double> gamma, std::optional<double> compassHeading, std::optional<double> compassAccuracy) { if (dispatched()) return; initEvent(type, bubbles, cancelable); - m_orientation = orientation; + m_orientation = DeviceOrientationData::create(alpha, beta, gamma, compassHeading, compassAccuracy); +} + +#else + +std::optional<bool> DeviceOrientationEvent::absolute() const +{ + return m_orientation->absolute(); } +void DeviceOrientationEvent::initDeviceOrientationEvent(const AtomicString& type, bool bubbles, bool cancelable, std::optional<double> alpha, std::optional<double> beta, std::optional<double> gamma, std::optional<bool> absolute) +{ + if (dispatched()) + return; + + initEvent(type, bubbles, cancelable); + m_orientation = DeviceOrientationData::create(alpha, beta, gamma, absolute); +} + +#endif + EventInterface DeviceOrientationEvent::eventInterface() const { #if ENABLE(DEVICE_ORIENTATION) diff --git a/Source/WebCore/dom/DeviceOrientationEvent.h b/Source/WebCore/dom/DeviceOrientationEvent.h index d69ddbf95..a5d70316c 100644 --- a/Source/WebCore/dom/DeviceOrientationEvent.h +++ b/Source/WebCore/dom/DeviceOrientationEvent.h @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DeviceOrientationEvent_h -#define DeviceOrientationEvent_h +#pragma once #include "Event.h" @@ -32,31 +31,42 @@ namespace WebCore { class DeviceOrientationData; -class DeviceOrientationEvent : public Event { +class DeviceOrientationEvent final : public Event { public: - ~DeviceOrientationEvent(); - static PassRefPtr<DeviceOrientationEvent> create() + static Ref<DeviceOrientationEvent> create(const AtomicString& eventType, DeviceOrientationData* orientation) { - return adoptRef(new DeviceOrientationEvent); + return adoptRef(*new DeviceOrientationEvent(eventType, orientation)); } - static PassRefPtr<DeviceOrientationEvent> create(const AtomicString& eventType, DeviceOrientationData* orientation) + + static Ref<DeviceOrientationEvent> createForBindings() { - return adoptRef(new DeviceOrientationEvent(eventType, orientation)); + return adoptRef(*new DeviceOrientationEvent); } - void initDeviceOrientationEvent(const AtomicString& type, bool bubbles, bool cancelable, DeviceOrientationData*); + virtual ~DeviceOrientationEvent(); + + std::optional<double> alpha() const; + std::optional<double> beta() const; + std::optional<double> gamma() const; + +#if PLATFORM(IOS) + std::optional<double> compassHeading() const; + std::optional<double> compassAccuracy() const; - DeviceOrientationData* orientation() const { return m_orientation.get(); } + void initDeviceOrientationEvent(const AtomicString& type, bool bubbles, bool cancelable, std::optional<double> alpha, std::optional<double> beta, std::optional<double> gamma, std::optional<double> compassHeading, std::optional<double> compassAccuracy); +#else + std::optional<bool> absolute() const; - virtual EventInterface eventInterface() const override; + void initDeviceOrientationEvent(const AtomicString& type, bool bubbles, bool cancelable, std::optional<double> alpha, std::optional<double> beta, std::optional<double> gamma, std::optional<bool> absolute); +#endif private: DeviceOrientationEvent(); DeviceOrientationEvent(const AtomicString& eventType, DeviceOrientationData*); + EventInterface eventInterface() const override; + RefPtr<DeviceOrientationData> m_orientation; }; } // namespace WebCore - -#endif // DeviceOrientationEvent_h diff --git a/Source/WebCore/dom/DeviceOrientationEvent.idl b/Source/WebCore/dom/DeviceOrientationEvent.idl index 2ab8ffafb..f349490d9 100644 --- a/Source/WebCore/dom/DeviceOrientationEvent.idl +++ b/Source/WebCore/dom/DeviceOrientationEvent.idl @@ -26,32 +26,32 @@ [ Conditional=DEVICE_ORIENTATION, ] interface DeviceOrientationEvent : Event { - [Custom] readonly attribute double alpha; - [Custom] readonly attribute double beta; - [Custom] readonly attribute double gamma; + readonly attribute unrestricted double? alpha; + readonly attribute unrestricted double? beta; + readonly attribute unrestricted double? gamma; // FIXME: Consider defining an ENABLE macro for iOS device orientation code and/or modifying // the bindings scripts to support generating more complicated conditional code. #if defined(WTF_PLATFORM_IOS) && WTF_PLATFORM_IOS - [Custom] readonly attribute double webkitCompassHeading; - [Custom] readonly attribute double webkitCompassAccuracy; - [Custom] void initDeviceOrientationEvent([Default=Undefined] optional DOMString type, - [Default=Undefined] optional boolean bubbles, - [Default=Undefined] optional boolean cancelable, - [Default=Undefined] optional double alpha, - [Default=Undefined] optional double beta, - [Default=Undefined] optional double gamma, - [Default=Undefined] optional double compassHeading, - [Default=Undefined] optional double compassAccuracy); + [ImplementedAs=compassHeading] readonly attribute unrestricted double? webkitCompassHeading; + [ImplementedAs=compassAccuracy] readonly attribute unrestricted double? webkitCompassAccuracy; + void initDeviceOrientationEvent(optional DOMString type = "", + optional boolean bubbles = false, + optional boolean cancelable = false, + optional unrestricted double? alpha = null, + optional unrestricted double? beta = null, + optional unrestricted double? gamma = null, + optional unrestricted double? compassHeading = null, + optional unrestricted double? compassAccuracy = null); #else - [Custom] readonly attribute boolean absolute; - [Custom] void initDeviceOrientationEvent([Default=Undefined] optional DOMString type, - [Default=Undefined] optional boolean bubbles, - [Default=Undefined] optional boolean cancelable, - [Default=Undefined] optional double alpha, - [Default=Undefined] optional double beta, - [Default=Undefined] optional double gamma, - [Default=Undefined] optional boolean absolute); + readonly attribute boolean? absolute; + void initDeviceOrientationEvent(optional DOMString type = "", + optional boolean bubbles = false, + optional boolean cancelable = false, + optional unrestricted double? alpha = null, + optional unrestricted double? beta = null, + optional unrestricted double? gamma = null, + optional boolean? absolute = null); #endif }; 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 = ¤tPage->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 diff --git a/Source/WebCore/dom/Document.h b/Source/WebCore/dom/Document.h index cf6f997af..f865de791 100644 --- a/Source/WebCore/dom/Document.h +++ b/Source/WebCore/dom/Document.h @@ -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, 2010, 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) 2010 Nokia Corporation and/or its subsidiary(-ies) * Copyright (C) 2011 Google Inc. All rights reserved. @@ -25,48 +25,60 @@ * */ -#ifndef Document_h -#define Document_h +#pragma once #include "CollectionType.h" #include "Color.h" #include "ContainerNode.h" #include "DocumentEventQueue.h" -#include "DocumentStyleSheetCollection.h" #include "DocumentTiming.h" +#include "ExceptionOr.h" #include "FocusDirection.h" -#include "HitTestRequest.h" -#include "IconURL.h" -#include "InspectorCounters.h" +#include "FontSelectorClient.h" +#include "FrameDestructionObserver.h" +#include "MediaProducer.h" #include "MutationObserver.h" #include "PageVisibilityState.h" -#include "PlatformScreen.h" -#include "QualifiedName.h" +#include "PlatformEvent.h" #include "ReferrerPolicy.h" +#include "Region.h" #include "RenderPtr.h" #include "ScriptExecutionContext.h" #include "StringWithDirection.h" -#include "StyleResolveTree.h" +#include "StyleChange.h" +#include "Supplementable.h" +#include "TextResourceDecoder.h" #include "Timer.h" #include "TreeScope.h" #include "UserActionElementSet.h" #include "ViewportArguments.h" #include <chrono> +#include <memory> #include <wtf/Deque.h> +#include <wtf/HashCountedSet.h> #include <wtf/HashSet.h> -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> -#include <wtf/PassRefPtr.h> #include <wtf/WeakPtr.h> +#include <wtf/text/AtomicStringHash.h> + +#if PLATFORM(IOS) +#include "EventTrackingRegions.h" +#endif + +namespace JSC { +class ExecState; +class InputCursor; +} namespace WebCore { class AXObjectCache; class Attr; class CDATASection; +class CSSFontSelector; class CSSStyleDeclaration; class CSSStyleSheet; class CachedCSSStyleSheet; +class CachedFrameBase; class CachedResourceLoader; class CachedScript; class CanvasRenderingContext; @@ -84,20 +96,18 @@ class DocumentLoader; class DocumentMarkerController; class DocumentParser; class DocumentSharedObjectPool; -class DocumentStyleSheetCollection; class DocumentType; -class Element; -class EntityReference; -class Event; -class EventListener; -class FloatRect; +class ExtensionStyleSheets; class FloatQuad; +class FloatRect; +class FontFaceSet; class FormController; class Frame; class FrameView; +class HTMLAllCollection; +class HTMLBodyElement; class HTMLCanvasElement; class HTMLCollection; -class HTMLAllCollection; class HTMLDocument; class HTMLElement; class HTMLFrameOwnerElement; @@ -105,17 +115,21 @@ class HTMLHeadElement; class HTMLIFrameElement; class HTMLImageElement; class HTMLMapElement; -class HTMLNameCollection; +class HTMLMediaElement; +class HTMLPictureElement; class HTMLScriptElement; class HitTestRequest; class HitTestResult; class IntPoint; +class JSNode; class LayoutPoint; class LayoutRect; class LiveNodeList; -class JSNode; class Locale; +class Location; class MediaCanStartListener; +class MediaPlaybackTarget; +class MediaPlaybackTargetClient; class MediaQueryList; class MediaQueryMatcher; class MouseEventWithHitTestResults; @@ -125,17 +139,23 @@ class NodeIterator; class Page; class PlatformMouseEvent; class ProcessingInstruction; +class QualifiedName; class Range; -class RegisteredEventListener; -class RenderView; class RenderFullScreen; -class ScriptableDocumentParser; +class RenderView; +class RequestAnimationFrameCallback; +class SVGDocumentExtensions; +class SVGSVGElement; class ScriptElementData; +class ScriptModuleLoader; class ScriptRunner; +class ScriptableDocumentParser; +class ScriptedAnimationController; class SecurityOrigin; +class SegmentedString; +class SelectorQuery; class SelectorQueryCache; class SerializedScriptValue; -class SegmentedString; class Settings; class StyleResolver; class StyleSheet; @@ -145,16 +165,14 @@ class Text; class TextResourceDecoder; class TreeWalker; class VisitedLinkState; -class WebKitNamedFlow; -class XMLHttpRequest; class XPathEvaluator; class XPathExpression; class XPathNSResolver; class XPathResult; -#if ENABLE(SVG) -class SVGDocumentExtensions; -#endif +enum class ShouldOpenExternalURLsPolicy; + +using PlatformDisplayID = uint32_t; #if ENABLE(XSLT) class TransformSource; @@ -164,33 +182,15 @@ class TransformSource; struct AnnotatedRegionValue; #endif -#if ENABLE(TOUCH_EVENTS) -#if PLATFORM(IOS) +#if ENABLE(IOS_TOUCH_EVENTS) #include <WebKitAdditions/DocumentIOSForward.h> -#endif // PLATFORM(IOS) -class Touch; -class TouchList; -#endif - -#if ENABLE(REQUEST_ANIMATION_FRAME) -class RequestAnimationFrameCallback; -class ScriptedAnimationController; -#endif - -#if ENABLE(TEXT_AUTOSIZING) -class TextAutosizer; -#endif - -#if ENABLE(CSP_NEXT) -class DOMSecurityPolicy; #endif -#if ENABLE(FONT_LOAD_EVENTS) -class FontLoader; +#if ENABLE(TOUCH_EVENTS) || ENABLE(IOS_TOUCH_EVENTS) +class Touch; +class TouchList; #endif -typedef int ExceptionCode; - #if PLATFORM(IOS) class DeviceMotionClient; class DeviceMotionController; @@ -198,7 +198,7 @@ class DeviceOrientationClient; class DeviceOrientationController; #endif -#if ENABLE(IOS_TEXT_AUTOSIZING) +#if ENABLE(TEXT_AUTOSIZING) struct TextAutoSizingHash; class TextAutoSizingKey; class TextAutoSizingValue; @@ -210,26 +210,35 @@ struct TextAutoSizingTraits : WTF::GenericHashTraits<TextAutoSizingKey> { }; #endif +#if ENABLE(MEDIA_SESSION) +class MediaSession; +#endif + +namespace Style { +class Scope; +}; + +const uint64_t HTMLMediaElementInvalidID = 0; + enum PageshowEventPersistence { PageshowEventNotPersisted = 0, PageshowEventPersisted = 1 }; -enum StyleResolverUpdateFlag { RecalcStyleImmediately, DeferRecalcStyle, RecalcStyleIfNeeded, DeferRecalcStyleIfNeeded }; - enum NodeListInvalidationType { DoNotInvalidateOnAttributeChanges = 0, InvalidateOnClassAttrChange, InvalidateOnIdNameAttrChange, InvalidateOnNameAttrChange, - InvalidateOnForAttrChange, + InvalidateOnForTypeAttrChange, InvalidateForFormControls, InvalidateOnHRefAttrChange, InvalidateOnAnyAttrChange, }; const int numNodeListInvalidationTypes = InvalidateOnAnyAttrChange + 1; -typedef HashCountedSet<Node*> TouchEventTargetSet; +enum class EventHandlerRemoval { One, All }; +typedef HashCountedSet<Node*> EventTargetSet; enum DocumentClass { DefaultDocumentClass = 0, @@ -239,117 +248,103 @@ enum DocumentClass { PluginDocumentClass = 1 << 3, MediaDocumentClass = 1 << 4, SVGDocumentClass = 1 << 5, + TextDocumentClass = 1 << 6, + XMLDocumentClass = 1 << 7, }; typedef unsigned char DocumentClassFlags; -class Document : public ContainerNode, public TreeScope, public ScriptExecutionContext { +enum class DocumentCompatibilityMode : unsigned char { + NoQuirksMode = 1, + QuirksMode = 1 << 1, + LimitedQuirksMode = 1 << 2 +}; + +enum DimensionsCheck { WidthDimensionsCheck = 1 << 0, HeightDimensionsCheck = 1 << 1, AllDimensionsCheck = 1 << 2 }; + +enum class SelectionRestorationMode { + Restore, + SetDefault, +}; + +enum class HttpEquivPolicy { + Enabled, + DisabledBySettings, + DisabledByContentDispositionAttachmentSandbox +}; + +enum class CustomElementNameValidationStatus { Valid, ConflictsWithBuiltinNames, NoHyphen, ContainsUpperCase }; + +class Document + : public ContainerNode + , public TreeScope + , public ScriptExecutionContext + , public FontSelectorClient + , public FrameDestructionObserver + , public Supplementable<Document> { public: - static PassRefPtr<Document> create(Frame* frame, const URL& url) + static Ref<Document> create(Frame* frame, const URL& url) { - return adoptRef(new Document(frame, url)); + return adoptRef(*new Document(frame, url)); } - static PassRefPtr<Document> createXHTML(Frame* frame, const URL& url) + + static Ref<Document> createNonRenderedPlaceholder(Frame* frame, const URL& url) + { + return adoptRef(*new Document(frame, url, DefaultDocumentClass, NonRenderedPlaceholder)); + } + static Ref<Document> create(Document&); + + virtual ~Document(); + + // Nodes belonging to this document increase referencingNodeCount - + // these are enough to keep the document from being destroyed, but + // not enough to keep it from removing its children. This allows a + // node that outlives its document to still have a valid document + // pointer without introducing reference cycles. + void incrementReferencingNodeCount() { - return adoptRef(new Document(frame, url, XHTMLDocumentClass)); + ASSERT(!m_deletionHasBegun); + ++m_referencingNodeCount; } - static PassRefPtr<Document> createNonRenderedPlaceholder(Frame* frame, const URL& url) + + void decrementReferencingNodeCount() { - return adoptRef(new Document(frame, url, DefaultDocumentClass, NonRenderedPlaceholder)); + ASSERT(!m_deletionHasBegun || !m_referencingNodeCount); + --m_referencingNodeCount; + if (!m_referencingNodeCount && !refCount()) { +#if !ASSERT_DISABLED + m_deletionHasBegun = true; +#endif + delete this; + } } - virtual ~Document(); + + unsigned referencingNodeCount() const { return m_referencingNodeCount; } + + void removedLastRef(); + + WEBCORE_EXPORT static HashSet<Document*>& allDocuments(); MediaQueryMatcher& mediaQueryMatcher(); using ContainerNode::ref; using ContainerNode::deref; + using TreeScope::rootNode; - virtual bool canContainRangeEndPoint() const override { return true; } + bool canContainRangeEndPoint() const final { return true; } Element* getElementByAccessKey(const String& key); void invalidateAccessKeyMap(); - void addImageElementByLowercasedUsemap(const AtomicStringImpl&, HTMLImageElement&); - void removeImageElementByLowercasedUsemap(const AtomicStringImpl&, HTMLImageElement&); - HTMLImageElement* imageElementByLowercasedUsemap(const AtomicStringImpl&) const; + void addImageElementByUsemap(const AtomicStringImpl&, HTMLImageElement&); + void removeImageElementByUsemap(const AtomicStringImpl&, HTMLImageElement&); + HTMLImageElement* imageElementByUsemap(const AtomicStringImpl&) const; - SelectorQueryCache& selectorQueryCache(); + ExceptionOr<SelectorQuery&> selectorQueryForString(const String&); + void clearSelectorQueryCache(); // DOM methods & attributes for Document - DEFINE_ATTRIBUTE_EVENT_LISTENER(abort); - DEFINE_ATTRIBUTE_EVENT_LISTENER(change); - DEFINE_ATTRIBUTE_EVENT_LISTENER(click); - DEFINE_ATTRIBUTE_EVENT_LISTENER(contextmenu); - DEFINE_ATTRIBUTE_EVENT_LISTENER(dblclick); - DEFINE_ATTRIBUTE_EVENT_LISTENER(dragenter); - DEFINE_ATTRIBUTE_EVENT_LISTENER(dragover); - DEFINE_ATTRIBUTE_EVENT_LISTENER(dragleave); - DEFINE_ATTRIBUTE_EVENT_LISTENER(drop); - DEFINE_ATTRIBUTE_EVENT_LISTENER(dragstart); - DEFINE_ATTRIBUTE_EVENT_LISTENER(drag); - DEFINE_ATTRIBUTE_EVENT_LISTENER(dragend); - DEFINE_ATTRIBUTE_EVENT_LISTENER(input); - DEFINE_ATTRIBUTE_EVENT_LISTENER(invalid); - DEFINE_ATTRIBUTE_EVENT_LISTENER(keydown); - DEFINE_ATTRIBUTE_EVENT_LISTENER(keypress); - DEFINE_ATTRIBUTE_EVENT_LISTENER(keyup); - DEFINE_ATTRIBUTE_EVENT_LISTENER(mousedown); - DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseenter); - DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseleave); - DEFINE_ATTRIBUTE_EVENT_LISTENER(mousemove); - DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseout); - DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseover); - DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseup); - DEFINE_ATTRIBUTE_EVENT_LISTENER(mousewheel); - DEFINE_ATTRIBUTE_EVENT_LISTENER(scroll); - DEFINE_ATTRIBUTE_EVENT_LISTENER(select); - DEFINE_ATTRIBUTE_EVENT_LISTENER(submit); - DEFINE_ATTRIBUTE_EVENT_LISTENER(wheel); - - DEFINE_ATTRIBUTE_EVENT_LISTENER(blur); - DEFINE_ATTRIBUTE_EVENT_LISTENER(error); - DEFINE_ATTRIBUTE_EVENT_LISTENER(focus); - DEFINE_ATTRIBUTE_EVENT_LISTENER(load); - DEFINE_ATTRIBUTE_EVENT_LISTENER(readystatechange); - - // WebKit extensions - DEFINE_ATTRIBUTE_EVENT_LISTENER(beforecut); - DEFINE_ATTRIBUTE_EVENT_LISTENER(cut); - DEFINE_ATTRIBUTE_EVENT_LISTENER(beforecopy); - DEFINE_ATTRIBUTE_EVENT_LISTENER(copy); - DEFINE_ATTRIBUTE_EVENT_LISTENER(beforepaste); - DEFINE_ATTRIBUTE_EVENT_LISTENER(paste); - DEFINE_ATTRIBUTE_EVENT_LISTENER(reset); - DEFINE_ATTRIBUTE_EVENT_LISTENER(search); - DEFINE_ATTRIBUTE_EVENT_LISTENER(selectstart); - DEFINE_ATTRIBUTE_EVENT_LISTENER(selectionchange); -#if ENABLE(TOUCH_EVENTS) - DEFINE_ATTRIBUTE_EVENT_LISTENER(touchstart); - DEFINE_ATTRIBUTE_EVENT_LISTENER(touchmove); - DEFINE_ATTRIBUTE_EVENT_LISTENER(touchend); - DEFINE_ATTRIBUTE_EVENT_LISTENER(touchcancel); -#endif -#if ENABLE(IOS_GESTURE_EVENTS) - DEFINE_ATTRIBUTE_EVENT_LISTENER(gesturestart); - DEFINE_ATTRIBUTE_EVENT_LISTENER(gesturechange); - DEFINE_ATTRIBUTE_EVENT_LISTENER(gestureend); -#endif -#if ENABLE(FULLSCREEN_API) - DEFINE_ATTRIBUTE_EVENT_LISTENER(webkitfullscreenchange); - DEFINE_ATTRIBUTE_EVENT_LISTENER(webkitfullscreenerror); -#endif -#if ENABLE(POINTER_LOCK) - DEFINE_ATTRIBUTE_EVENT_LISTENER(webkitpointerlockchange); - DEFINE_ATTRIBUTE_EVENT_LISTENER(webkitpointerlockerror); -#endif -#if ENABLE(PAGE_VISIBILITY_API) - DEFINE_ATTRIBUTE_EVENT_LISTENER(visibilitychange); -#endif -#if ENABLE(CSP_NEXT) - DEFINE_ATTRIBUTE_EVENT_LISTENER(securitypolicyviolation); -#endif - void setViewportArguments(const ViewportArguments& viewportArguments) { m_viewportArguments = viewportArguments; } ViewportArguments viewportArguments() const { return m_viewportArguments; } #ifndef NDEBUG @@ -359,63 +354,66 @@ public: void setReferrerPolicy(ReferrerPolicy referrerPolicy) { m_referrerPolicy = referrerPolicy; } ReferrerPolicy referrerPolicy() const { return m_referrerPolicy; } - DocumentType* doctype() const; + WEBCORE_EXPORT DocumentType* doctype() const; - DOMImplementation* implementation(); + WEBCORE_EXPORT DOMImplementation& implementation(); Element* documentElement() const { return m_documentElement.get(); } + static ptrdiff_t documentElementMemoryOffset() { return OBJECT_OFFSETOF(Document, m_documentElement); } + + WEBCORE_EXPORT Element* activeElement(); + WEBCORE_EXPORT bool hasFocus() const; bool hasManifest() const; - virtual PassRefPtr<Element> createElement(const AtomicString& tagName, ExceptionCode&); - PassRefPtr<DocumentFragment> createDocumentFragment(); - PassRefPtr<Text> createTextNode(const String& data); - PassRefPtr<Comment> createComment(const String& data); - PassRefPtr<CDATASection> createCDATASection(const String& data, ExceptionCode&); - PassRefPtr<ProcessingInstruction> createProcessingInstruction(const String& target, const String& data, ExceptionCode&); - PassRefPtr<Attr> createAttribute(const String& name, ExceptionCode&); - PassRefPtr<Attr> createAttributeNS(const String& namespaceURI, const String& qualifiedName, ExceptionCode&, bool shouldIgnoreNamespaceChecks = false); - PassRefPtr<EntityReference> createEntityReference(const String& name, ExceptionCode&); - PassRefPtr<Node> importNode(Node* importedNode, ExceptionCode& ec) { return importNode(importedNode, true, ec); } - PassRefPtr<Node> importNode(Node* importedNode, bool deep, ExceptionCode&); - PassRefPtr<Element> createElementNS(const String& namespaceURI, const String& qualifiedName, ExceptionCode&); - PassRefPtr<Element> createElement(const QualifiedName&, bool createdByParser); - - bool cssStickyPositionEnabled() const; - bool cssRegionsEnabled() const; - bool cssCompositingEnabled() const; + WEBCORE_EXPORT ExceptionOr<Ref<Element>> createElementForBindings(const AtomicString& tagName); + WEBCORE_EXPORT Ref<DocumentFragment> createDocumentFragment(); + WEBCORE_EXPORT Ref<Text> createTextNode(const String& data); + WEBCORE_EXPORT Ref<Comment> createComment(const String& data); + WEBCORE_EXPORT ExceptionOr<Ref<CDATASection>> createCDATASection(const String& data); + WEBCORE_EXPORT ExceptionOr<Ref<ProcessingInstruction>> createProcessingInstruction(const String& target, const String& data); + WEBCORE_EXPORT ExceptionOr<Ref<Attr>> createAttribute(const String& name); + WEBCORE_EXPORT ExceptionOr<Ref<Attr>> createAttributeNS(const AtomicString& namespaceURI, const String& qualifiedName, bool shouldIgnoreNamespaceChecks = false); + WEBCORE_EXPORT ExceptionOr<Ref<Node>> importNode(Node& nodeToImport, bool deep); + WEBCORE_EXPORT ExceptionOr<Ref<Element>> createElementNS(const AtomicString& namespaceURI, const String& qualifiedName); + WEBCORE_EXPORT Ref<Element> createElement(const QualifiedName&, bool createdByParser); + + static CustomElementNameValidationStatus validateCustomElementName(const AtomicString&); + + bool isCSSGridLayoutEnabled() const; #if ENABLE(CSS_REGIONS) - PassRefPtr<DOMNamedFlowCollection> webkitGetNamedFlows(); + RefPtr<DOMNamedFlowCollection> webkitGetNamedFlows(); #endif - NamedFlowCollection* namedFlows(); + NamedFlowCollection& namedFlows(); - bool regionBasedColumnsEnabled() const; + WEBCORE_EXPORT RefPtr<Range> caretRangeFromPoint(int x, int y); + RefPtr<Range> caretRangeFromPoint(const LayoutPoint& clientPoint); - bool cssGridLayoutEnabled() const; + WEBCORE_EXPORT Element* scrollingElement(); - Element* elementFromPoint(int x, int y) const; - PassRefPtr<Range> caretRangeFromPoint(int x, int y); + WEBCORE_EXPORT String readyState() const; - String readyState() const; + WEBCORE_EXPORT String defaultCharsetForLegacyBindings() const; - String defaultCharset() const; - - String inputEncoding() const { return Document::encoding(); } String charset() const { return Document::encoding(); } - String characterSet() const { return Document::encoding(); } + WEBCORE_EXPORT String characterSetWithUTF8Fallback() const; + TextEncoding textEncoding() const; - String encoding() const; + AtomicString encoding() const { return textEncoding().domName(); } - void setCharset(const String&); + WEBCORE_EXPORT void setCharset(const String&); // Used by ObjC / GOBject bindings only. void setContent(const String&); String suggestedMIMEType() const; + void overrideMIMEType(const String&); + WEBCORE_EXPORT String contentType() const; + String contentLanguage() const { return m_contentLanguage; } void setContentLanguage(const String&); @@ -427,174 +425,166 @@ public: bool hasXMLDeclaration() const { return m_hasXMLDeclaration; } void setXMLEncoding(const String& encoding) { m_xmlEncoding = encoding; } // read-only property, only to be set from XMLDocumentParser - void setXMLVersion(const String&, ExceptionCode&); - void setXMLStandalone(bool, ExceptionCode&); + WEBCORE_EXPORT ExceptionOr<void> setXMLVersion(const String&); + WEBCORE_EXPORT void setXMLStandalone(bool); void setHasXMLDeclaration(bool hasXMLDeclaration) { m_hasXMLDeclaration = hasXMLDeclaration ? 1 : 0; } String documentURI() const { return m_documentURI; } - void setDocumentURI(const String&); + WEBCORE_EXPORT void setDocumentURI(const String&); - virtual URL baseURI() const override; +#if ENABLE(WEB_REPLAY) + JSC::InputCursor& inputCursor(); + void setInputCursor(Ref<JSC::InputCursor>&&); +#endif -#if ENABLE(PAGE_VISIBILITY_API) + using VisibilityState = PageVisibilityState; + WEBCORE_EXPORT VisibilityState visibilityState() const; void visibilityStateChanged(); - String visibilityState() const; - bool hidden() const; -#endif + WEBCORE_EXPORT bool hidden() const; -#if ENABLE(CSP_NEXT) - DOMSecurityPolicy* securityPolicy(); -#endif + void setTimerThrottlingEnabled(bool); + bool isTimerThrottlingEnabled() const { return m_isTimerThrottlingEnabled; } - PassRefPtr<Node> adoptNode(PassRefPtr<Node> source, ExceptionCode&); + WEBCORE_EXPORT ExceptionOr<Ref<Node>> adoptNode(Node& source); - PassRefPtr<HTMLCollection> images(); - PassRefPtr<HTMLCollection> embeds(); - PassRefPtr<HTMLCollection> plugins(); // an alias for embeds() required for the JS DOM bindings. - PassRefPtr<HTMLCollection> applets(); - PassRefPtr<HTMLCollection> links(); - PassRefPtr<HTMLCollection> forms(); - PassRefPtr<HTMLCollection> anchors(); - PassRefPtr<HTMLCollection> scripts(); - PassRefPtr<HTMLCollection> all(); + WEBCORE_EXPORT Ref<HTMLCollection> images(); + WEBCORE_EXPORT Ref<HTMLCollection> embeds(); + WEBCORE_EXPORT Ref<HTMLCollection> plugins(); // an alias for embeds() required for the JS DOM bindings. + WEBCORE_EXPORT Ref<HTMLCollection> applets(); + WEBCORE_EXPORT Ref<HTMLCollection> links(); + WEBCORE_EXPORT Ref<HTMLCollection> forms(); + WEBCORE_EXPORT Ref<HTMLCollection> anchors(); + WEBCORE_EXPORT Ref<HTMLCollection> scripts(); + Ref<HTMLCollection> all(); - PassRefPtr<HTMLCollection> windowNamedItems(const AtomicString& name); - PassRefPtr<HTMLCollection> documentNamedItems(const AtomicString& name); + Ref<HTMLCollection> windowNamedItems(const AtomicString& name); + Ref<HTMLCollection> documentNamedItems(const AtomicString& name); // Other methods (not part of DOM) bool isSynthesized() const { return m_isSynthesized; } bool isHTMLDocument() const { return m_documentClasses & HTMLDocumentClass; } bool isXHTMLDocument() const { return m_documentClasses & XHTMLDocumentClass; } + bool isXMLDocument() const { return m_documentClasses & XMLDocumentClass; } bool isImageDocument() const { return m_documentClasses & ImageDocumentClass; } bool isSVGDocument() const { return m_documentClasses & SVGDocumentClass; } bool isPluginDocument() const { return m_documentClasses & PluginDocumentClass; } bool isMediaDocument() const { return m_documentClasses & MediaDocumentClass; } -#if ENABLE(SVG) + bool isTextDocument() const { return m_documentClasses & TextDocumentClass; } bool hasSVGRootNode() const; -#else - static bool hasSVGRootNode() { return false; } -#endif virtual bool isFrameSet() const { return false; } - bool isSrcdocDocument() const { return m_isSrcdocDocument; } - - StyleResolver* styleResolverIfExists() const { return m_styleResolver.get(); } + static ptrdiff_t documentClassesMemoryOffset() { return OBJECT_OFFSETOF(Document, m_documentClasses); } + static uint32_t isHTMLDocumentClassFlag() { return HTMLDocumentClass; } - bool isViewSource() const { return m_isViewSource; } - void setIsViewSource(bool); + bool isSrcdocDocument() const { return m_isSrcdocDocument; } bool sawElementsInKnownNamespaces() const { return m_sawElementsInKnownNamespaces; } - StyleResolver& ensureStyleResolver() - { - if (!m_styleResolver) - createStyleResolver(); - return *m_styleResolver; - } + StyleResolver& userAgentShadowTreeStyleResolver(); - void notifyRemovePendingSheetIfNeeded(); + CSSFontSelector& fontSelector() { return m_fontSelector; } - bool haveStylesheetsLoaded() const; + WEBCORE_EXPORT bool haveStylesheetsLoaded() const; - // This is a DOM function. - StyleSheetList* styleSheets(); + WEBCORE_EXPORT StyleSheetList& styleSheets(); - DocumentStyleSheetCollection& styleSheetCollection() { return m_styleSheetCollection; } + Style::Scope& styleScope() { return *m_styleScope; } + const Style::Scope& styleScope() const { return *m_styleScope; } + ExtensionStyleSheets& extensionStyleSheets() { return *m_extensionStyleSheets; } + const ExtensionStyleSheets& extensionStyleSheets() const { return *m_extensionStyleSheets; } bool gotoAnchorNeededAfterStylesheetsLoad() { return m_gotoAnchorNeededAfterStylesheetsLoad; } void setGotoAnchorNeededAfterStylesheetsLoad(bool b) { m_gotoAnchorNeededAfterStylesheetsLoad = b; } - /** - * Called when one or more stylesheets in the document may have been added, removed or changed. - * - * Creates a new style resolver and assign it to this document. This is done by iterating through all nodes in - * document (or those before <BODY> in a HTML document), searching for stylesheets. Stylesheets can be contained in - * <LINK>, <STYLE> or <BODY> elements, as well as processing instructions (XML documents only). A list is - * constructed from these which is used to create the a new style selector which collates all of the stylesheets - * found and is used to calculate the derived styles for all rendering objects. - */ - void styleResolverChanged(StyleResolverUpdateFlag); - - void scheduleOptimizedStyleSheetUpdate(); - - void didAccessStyleResolver(); - void evaluateMediaQueryList(); FormController& formController(); Vector<String> formElementsState() const; void setStateForNewFormElements(const Vector<String>&); - FrameView* view() const; // can be NULL - Frame* frame() const { return m_frame; } // can be NULL - Page* page() const; // can be NULL - Settings* settings() const; // can be NULL + WEBCORE_EXPORT FrameView* view() const; // can be NULL + WEBCORE_EXPORT Page* page() const; // can be NULL + const Settings& settings() const { return m_settings.get(); } + Settings& mutableSettings() { return m_settings.get(); } float deviceScaleFactor() const; - PassRefPtr<Range> createRange(); + WEBCORE_EXPORT Ref<Range> createRange(); - PassRefPtr<NodeIterator> createNodeIterator(Node* root, unsigned whatToShow, - PassRefPtr<NodeFilter>, bool expandEntityReferences, ExceptionCode&); + // The last bool parameter is for ObjC bindings. + WEBCORE_EXPORT Ref<NodeIterator> createNodeIterator(Node& root, unsigned long whatToShow = 0xFFFFFFFF, RefPtr<NodeFilter>&& = nullptr, bool = false); - PassRefPtr<TreeWalker> createTreeWalker(Node* root, unsigned whatToShow, - PassRefPtr<NodeFilter>, bool expandEntityReferences, ExceptionCode&); + // The last bool parameter is for ObjC bindings. + WEBCORE_EXPORT Ref<TreeWalker> createTreeWalker(Node& root, unsigned long whatToShow = 0xFFFFFFFF, RefPtr<NodeFilter>&& = nullptr, bool = false); // Special support for editing - PassRefPtr<CSSStyleDeclaration> createCSSStyleDeclaration(); - PassRefPtr<Text> createEditingTextNode(const String&); + WEBCORE_EXPORT Ref<CSSStyleDeclaration> createCSSStyleDeclaration(); + Ref<Text> createEditingTextNode(const String&); void recalcStyle(Style::Change = Style::NoChange); - void updateStyleIfNeeded(); - void updateLayout(); - enum RunPostLayoutTasks { - RunPostLayoutTasksAsynchronously, - RunPostLayoutTasksSynchronously, + WEBCORE_EXPORT void updateStyleIfNeeded(); + bool needsStyleRecalc() const; + unsigned lastStyleUpdateSizeForTesting() const { return m_lastStyleUpdateSizeForTesting; } + + WEBCORE_EXPORT void updateLayout(); + + // updateLayoutIgnorePendingStylesheets() forces layout even if we are waiting for pending stylesheet loads, + // so calling this may cause a flash of unstyled content (FOUC). + enum class RunPostLayoutTasks { + Asynchronously, + Synchronously, }; - void updateLayoutIgnorePendingStylesheets(RunPostLayoutTasks = RunPostLayoutTasksAsynchronously); - PassRef<RenderStyle> styleForElementIgnoringPendingStylesheets(Element*); + WEBCORE_EXPORT void updateLayoutIgnorePendingStylesheets(RunPostLayoutTasks = RunPostLayoutTasks::Asynchronously); + + std::unique_ptr<RenderStyle> styleForElementIgnoringPendingStylesheets(Element&, const RenderStyle* parentStyle); // Returns true if page box (margin boxes and page borders) is visible. - bool isPageBoxVisible(int pageIndex); + WEBCORE_EXPORT bool isPageBoxVisible(int pageIndex); // Returns the preferred page size and margins in pixels, assuming 96 // pixels per inch. pageSize, marginTop, marginRight, marginBottom, // marginLeft must be initialized to the default values that are used if // auto is specified. - void pageSizeAndMarginsInPixels(int pageIndex, IntSize& pageSize, int& marginTop, int& marginRight, int& marginBottom, int& marginLeft); + WEBCORE_EXPORT void pageSizeAndMarginsInPixels(int pageIndex, IntSize& pageSize, int& marginTop, int& marginRight, int& marginBottom, int& marginLeft); - CachedResourceLoader* cachedResourceLoader() { return m_cachedResourceLoader.get(); } + CachedResourceLoader& cachedResourceLoader() { return m_cachedResourceLoader; } void didBecomeCurrentDocumentInFrame(); void destroyRenderTree(); - void disconnectFromFrame(); void prepareForDestruction(); + void didBecomeCurrentDocumentInView(); // Override ScriptExecutionContext methods to do additional work - virtual void suspendActiveDOMObjects(ActiveDOMObject::ReasonForSuspension) override; - virtual void resumeActiveDOMObjects(ActiveDOMObject::ReasonForSuspension) override; - virtual void stopActiveDOMObjects() override; + bool shouldBypassMainWorldContentSecurityPolicy() const final; + void suspendActiveDOMObjects(ActiveDOMObject::ReasonForSuspension) final; + void resumeActiveDOMObjects(ActiveDOMObject::ReasonForSuspension) final; + void stopActiveDOMObjects() final; + + void suspendDeviceMotionAndOrientationUpdates(); + void resumeDeviceMotionAndOrientationUpdates(); RenderView* renderView() const { return m_renderView.get(); } bool renderTreeBeingDestroyed() const { return m_renderTreeBeingDestroyed; } bool hasLivingRenderTree() const { return renderView() && !renderTreeBeingDestroyed(); } - + + bool updateLayoutIfDimensionsOutOfDate(Element&, DimensionsCheck = AllDimensionsCheck); + AXObjectCache* existingAXObjectCache() const; - AXObjectCache* axObjectCache() const; + WEBCORE_EXPORT AXObjectCache* axObjectCache() const; void clearAXObjectCache(); // to get visually ordered hebrew and arabic pages right void setVisuallyOrdered(); bool visuallyOrdered() const { return m_visuallyOrdered; } - DocumentLoader* loader() const; + WEBCORE_EXPORT DocumentLoader* loader() const; - void open(Document* ownerDocument = 0); + WEBCORE_EXPORT void open(Document* ownerDocument = nullptr); void implicitOpen(); // close() is the DOM API document.close() - void close(); + WEBCORE_EXPORT void close(); // In some situations (see the code), we ignore document.close(). // explicitClose() bypass these checks and actually tries to close the // input stream. @@ -604,14 +594,15 @@ public: void cancelParsing(); - void write(const SegmentedString& text, Document* ownerDocument = 0); - void write(const String& text, Document* ownerDocument = 0); - void writeln(const String& text, Document* ownerDocument = 0); + void write(SegmentedString&& text, Document* ownerDocument = nullptr); + WEBCORE_EXPORT void write(const String& text, Document* ownerDocument = nullptr); + WEBCORE_EXPORT void writeln(const String& text, Document* ownerDocument = nullptr); bool wellFormed() const { return m_wellFormed; } - virtual const URL& url() const override final { return m_url; } + const URL& url() const final { return m_url; } void setURL(const URL&); + const URL& urlForBindings() const { return m_url.isEmpty() ? blankURL() : m_url; } // To understand how these concepts relate to one another, please see the // comments surrounding their declaration. @@ -622,19 +613,27 @@ public: const String& baseTarget() const { return m_baseTarget; } void processBaseElement(); - virtual URL completeURL(const String&) const override final; + WEBCORE_EXPORT URL completeURL(const String&) const final; URL completeURL(const String&, const URL& baseURLOverride) const; - virtual String userAgent(const URL&) const override; + String userAgent(const URL&) const final; - virtual void disableEval(const String& errorMessage) override; + void disableEval(const String& errorMessage) final; + +#if ENABLE(INDEXED_DATABASE) + IDBClient::IDBConnectionProxy* idbConnectionProxy() final; +#endif +#if ENABLE(WEB_SOCKETS) + SocketProvider* socketProvider() final; +#endif bool canNavigate(Frame* targetFrame); Frame* findUnsafeParentScrollPropagationBoundary(); - CSSStyleSheet& elementSheet(); + bool usesStyleBasedEditability() const; + void setHasElementUsingStyleBasedEditability(); - virtual PassRefPtr<DocumentParser> createParser(); + virtual Ref<DocumentParser> createParser(); DocumentParser* parser() const { return m_parser.get(); } ScriptableDocumentParser* scriptableDocumentParser() const; @@ -646,17 +645,15 @@ public: bool paginated() const { return printing() || paginatedForScreen(); } - enum CompatibilityMode { QuirksMode, LimitedQuirksMode, NoQuirksMode }; - - void setCompatibilityMode(CompatibilityMode m); + void setCompatibilityMode(DocumentCompatibilityMode); void lockCompatibilityMode() { m_compatibilityModeLocked = true; } - CompatibilityMode compatibilityMode() const { return m_compatibilityMode; } + static ptrdiff_t compatibilityModeMemoryOffset() { return OBJECT_OFFSETOF(Document, m_compatibilityMode); } - String compatMode() const; + WEBCORE_EXPORT String compatMode() const; - bool inQuirksMode() const { return m_compatibilityMode == QuirksMode; } - bool inLimitedQuirksMode() const { return m_compatibilityMode == LimitedQuirksMode; } - bool inNoQuirksMode() const { return m_compatibilityMode == NoQuirksMode; } + bool inQuirksMode() const { return m_compatibilityMode == DocumentCompatibilityMode::QuirksMode; } + bool inLimitedQuirksMode() const { return m_compatibilityMode == DocumentCompatibilityMode::LimitedQuirksMode; } + bool inNoQuirksMode() const { return m_compatibilityMode == DocumentCompatibilityMode::NoQuirksMode; } enum ReadyState { Loading, @@ -666,14 +663,14 @@ public: void setReadyState(ReadyState); void setParsing(bool); bool parsing() const { return m_bParsing; } - std::chrono::milliseconds minimumLayoutDelay(); + Seconds minimumLayoutDelay(); bool shouldScheduleLayout(); bool isLayoutTimerActive(); - std::chrono::milliseconds elapsedTime() const; + Seconds timeSinceDocumentCreation() const; void setTextColor(const Color& color) { m_textColor = color; } - Color textColor() const { return m_textColor; } + const Color& textColor() const { return m_textColor; } const Color& linkColor() const { return m_linkColor; } const Color& visitedLinkColor() const { return m_visitedLinkColor; } @@ -688,52 +685,49 @@ public: MouseEventWithHitTestResults prepareMouseEvent(const HitTestRequest&, const LayoutPoint&, const PlatformMouseEvent&); - /* Newly proposed CSS3 mechanism for selecting alternate - stylesheets using the DOM. May be subject to change as - spec matures. - dwh - */ - String preferredStylesheetSet() const; - String selectedStylesheetSet() const; - void setSelectedStylesheetSet(const String&); + WEBCORE_EXPORT String preferredStylesheetSet() const; + WEBCORE_EXPORT String selectedStylesheetSet() const; + WEBCORE_EXPORT void setSelectedStylesheetSet(const String&); - bool setFocusedElement(PassRefPtr<Element>, FocusDirection = FocusDirectionNone); + enum class FocusRemovalEventsMode { Dispatch, DoNotDispatch }; + WEBCORE_EXPORT bool setFocusedElement(Element*, FocusDirection = FocusDirectionNone, + FocusRemovalEventsMode = FocusRemovalEventsMode::Dispatch); Element* focusedElement() const { return m_focusedElement.get(); } UserActionElementSet& userActionElements() { return m_userActionElements; } const UserActionElementSet& userActionElements() const { return m_userActionElements; } - // The m_ignoreAutofocus flag specifies whether or not the document has been changed by the user enough - // for WebCore to ignore the autofocus attribute on any form controls - bool ignoreAutofocus() const { return m_ignoreAutofocus; }; - void setIgnoreAutofocus(bool shouldIgnore = true) { m_ignoreAutofocus = shouldIgnore; }; + void setFocusNavigationStartingNode(Node*); + Element* focusNavigationStartingNode(FocusDirection) const; - void removeFocusedNodeOfSubtree(Node*, bool amongChildrenOnly = false); + void removeFocusedNodeOfSubtree(Node&, bool amongChildrenOnly = false); void hoveredElementDidDetach(Element*); void elementInActiveChainDidDetach(Element*); - void updateHoverActiveState(const HitTestRequest&, Element*, const PlatformMouseEvent* = 0, StyleResolverUpdateFlag = RecalcStyleIfNeeded); + void updateHoverActiveState(const HitTestRequest&, Element*); // Updates for :target (CSS3 selector). void setCSSTarget(Element*); Element* cssTarget() const { return m_cssTarget; } - - void scheduleForcedStyleRecalc(); + static ptrdiff_t cssTargetMemoryOffset() { return OBJECT_OFFSETOF(Document, m_cssTarget); } + + WEBCORE_EXPORT void scheduleForcedStyleRecalc(); void scheduleStyleRecalc(); void unscheduleStyleRecalc(); bool hasPendingStyleRecalc() const; bool hasPendingForcedStyleRecalc() const; - void styleRecalcTimerFired(Timer<Document>&); - void optimizedStyleSheetUpdateTimerFired(Timer<Document>&); - void registerNodeList(LiveNodeList&); - void unregisterNodeList(LiveNodeList&); - void registerCollection(HTMLCollection&); + void registerNodeListForInvalidation(LiveNodeList&); + void unregisterNodeListForInvalidation(LiveNodeList&); + WEBCORE_EXPORT void registerCollection(HTMLCollection&); void unregisterCollection(HTMLCollection&); + void collectionCachedIdNameMap(const HTMLCollection&); + void collectionWillClearIdNameMap(const HTMLCollection&); bool shouldInvalidateNodeListAndCollectionCaches(const QualifiedName* attrName = nullptr) const; void invalidateNodeListAndCollectionCaches(const QualifiedName* attrName); void attachNodeIterator(NodeIterator*); void detachNodeIterator(NodeIterator*); - void moveNodeIteratorsToNewDocument(Node*, Document*); + void moveNodeIteratorsToNewDocument(Node&, Document&); void attachRange(Range*); void detachRange(Range*); @@ -742,8 +736,10 @@ public: // nodeChildrenWillBeRemoved is used when removing all node children at once. void nodeChildrenWillBeRemoved(ContainerNode&); // nodeWillBeRemoved is only safe when removing one node at a time. - void nodeWillBeRemoved(Node*); - bool canReplaceChild(Node* newChild, Node* oldChild); + void nodeWillBeRemoved(Node&); + void removeFocusNavigationNodeOfSubtree(Node&, bool amongChildrenOnly = false); + enum class AcceptChildOperation { Replace, InsertOrAdd }; + bool canAcceptChild(const Node& newChild, const Node* refChild, AcceptChildOperation) const; void textInserted(Node*, unsigned offset, unsigned length); void textRemoved(Node*, unsigned offset, unsigned length); @@ -757,14 +753,17 @@ public: // In DOM Level 2, the Document's DOMWindow is called the defaultView. DOMWindow* defaultView() const { return domWindow(); } + Document& contextDocument() const; + void setContextDocument(Document& document) { m_contextDocument = document.createWeakPtr(); } + // Helper functions for forwarding DOMWindow event related tasks to the DOMWindow if it exists. - void setWindowAttributeEventListener(const AtomicString& eventType, const QualifiedName& attributeName, const AtomicString& value); - void setWindowAttributeEventListener(const AtomicString& eventType, PassRefPtr<EventListener>); - EventListener* getWindowAttributeEventListener(const AtomicString& eventType); - void dispatchWindowEvent(PassRefPtr<Event>, PassRefPtr<EventTarget> = 0); + void setWindowAttributeEventListener(const AtomicString& eventType, const QualifiedName& attributeName, const AtomicString& value, DOMWrapperWorld&); + void setWindowAttributeEventListener(const AtomicString& eventType, RefPtr<EventListener>&&, DOMWrapperWorld&); + EventListener* getWindowAttributeEventListener(const AtomicString& eventType, DOMWrapperWorld&); + WEBCORE_EXPORT void dispatchWindowEvent(Event&, EventTarget* = nullptr); void dispatchWindowLoadEvent(); - PassRefPtr<Event> createEvent(const String& eventType, ExceptionCode&); + WEBCORE_EXPORT ExceptionOr<Ref<Event>> createEvent(const String& eventType); // keep track of what types of event listeners are registered, so we don't // dispatch events unnecessarily @@ -781,11 +780,15 @@ public: ANIMATIONITERATION_LISTENER = 1 << 9, TRANSITIONEND_LISTENER = 1 << 10, BEFORELOAD_LISTENER = 1 << 11, - SCROLL_LISTENER = 1 << 12 - // 3 bits remaining + SCROLL_LISTENER = 1 << 12, + FORCEWILLBEGIN_LISTENER = 1 << 13, + FORCECHANGED_LISTENER = 1 << 14, + FORCEDOWN_LISTENER = 1 << 15, + FORCEUP_LISTENER = 1 << 16 }; bool hasListenerType(ListenerType listenerType) const { return (m_listenerTypes & listenerType); } + bool hasListenerTypeForEventType(PlatformEvent::Type) const; void addListenerTypeIfNeeded(const AtomicString& eventType); bool hasMutationObserversOfType(MutationObserver::MutationType type) const @@ -795,18 +798,13 @@ public: bool hasMutationObservers() const { return m_mutationObserverTypes; } void addMutationObserverTypes(MutationObserverOptions types) { m_mutationObserverTypes |= types; } - CSSStyleDeclaration* getOverrideStyle(Element*, const String& pseudoElt); + WEBCORE_EXPORT CSSStyleDeclaration* getOverrideStyle(Element*, const String& pseudoElt); - /** - * Handles a HTTP header equivalent set by a meta tag using <meta http-equiv="..." content="...">. This is called - * when a meta tag is encountered during document parsing, and also when a script dynamically changes or adds a meta - * tag. This enables scripts to use meta tags to perform refreshes and set expiry dates in addition to them being - * specified in a HTML file. - * - * @param equiv The http header name (value of the meta tag's "equiv" attribute) - * @param content The header value (value of the meta tag's "content" attribute) - */ - void processHttpEquiv(const String& equiv, const String& content); + // Handles an HTTP header equivalent set by a meta tag using <meta http-equiv="..." content="...">. This is called + // when a meta tag is encountered during document parsing, and also when a script dynamically changes or adds a meta + // tag. This enables scripts to use meta tags to perform refreshes and set expiry dates in addition to them being + // specified in an HTML file. + void processHttpEquiv(const String& equiv, const String& content, bool isInDocumentHead); #if PLATFORM(IOS) void processFormatDetection(const String&); @@ -823,25 +821,28 @@ public: // Returns 0 if this is the top level document. HTMLFrameOwnerElement* ownerElement() const; - HTMLIFrameElement* seamlessParentIFrame() const; - bool shouldDisplaySeamlesslyWithParent() const; - // Used by DOM bindings; no direction known. - String title() const { return m_title.string(); } - void setTitle(const String&); + const String& title() const { return m_title.string; } + WEBCORE_EXPORT void setTitle(const String&); - void setTitleElement(const StringWithDirection&, Element* titleElement); - void removeTitle(Element* titleElement); + WEBCORE_EXPORT const AtomicString& dir() const; + WEBCORE_EXPORT void setDir(const AtomicString&); - String cookie(ExceptionCode&) const; - void setCookie(const String&, ExceptionCode&); + void titleElementAdded(Element& titleElement); + void titleElementRemoved(Element& titleElement); + void titleElementTextChanged(Element& titleElement); - String referrer() const; + WEBCORE_EXPORT ExceptionOr<String> cookie(); + WEBCORE_EXPORT ExceptionOr<void> setCookie(const String&); - String domain() const; - void setDomain(const String& newDomain, ExceptionCode&); + WEBCORE_EXPORT String referrer() const; - String lastModified() const; + WEBCORE_EXPORT String origin() const; + + WEBCORE_EXPORT String domain() const; + ExceptionOr<void> setDomain(const String& newDomain); + + WEBCORE_EXPORT String lastModified(); // The cookieURL is used to query the cookie database for this document's // cookies. For example, if the cookie URL is http://example.com, we'll @@ -854,7 +855,7 @@ public: // inherits its cookieURL but not its URL. // const URL& cookieURL() const { return m_cookieURL; } - void setCookieURL(const URL& url) { m_cookieURL = url; } + void setCookieURL(const URL&); // The firstPartyForCookies is used to compute whether this document // appears in a "third-party" context for the purpose of third-party @@ -875,54 +876,55 @@ public: static bool isValidName(const String&); // The following breaks a qualified name into a prefix and a local name. - // It also does a validity check, and returns false if the qualified name - // is invalid. It also sets ExceptionCode when name is invalid. - static bool parseQualifiedName(const String& qualifiedName, String& prefix, String& localName, ExceptionCode&); + // It also does a validity check, and returns an error if the qualified name is invalid. + static ExceptionOr<std::pair<AtomicString, AtomicString>> parseQualifiedName(const String& qualifiedName); + static ExceptionOr<QualifiedName> parseQualifiedName(const AtomicString& namespaceURI, const String& qualifiedName); // Checks to make sure prefix and namespace do not conflict (per DOM Core 3) static bool hasValidNamespaceForElements(const QualifiedName&); static bool hasValidNamespaceForAttributes(const QualifiedName&); - HTMLElement* body() const; - void setBody(PassRefPtr<HTMLElement>, ExceptionCode&); + WEBCORE_EXPORT HTMLBodyElement* body() const; + WEBCORE_EXPORT HTMLElement* bodyOrFrameset() const; + WEBCORE_EXPORT ExceptionOr<void> setBodyOrFrameset(RefPtr<HTMLElement>&&); - HTMLHeadElement* head(); + Location* location() const; - DocumentMarkerController& markers() const { return *m_markers; } + WEBCORE_EXPORT HTMLHeadElement* head(); - bool directionSetOnDocumentElement() const { return m_directionSetOnDocumentElement; } - bool writingModeSetOnDocumentElement() const { return m_writingModeSetOnDocumentElement; } - void setDirectionSetOnDocumentElement(bool b) { m_directionSetOnDocumentElement = b; } - void setWritingModeSetOnDocumentElement(bool b) { m_writingModeSetOnDocumentElement = b; } + DocumentMarkerController& markers() const { return *m_markers; } - bool execCommand(const String& command, bool userInterface = false, const String& value = String()); - bool queryCommandEnabled(const String& command); - bool queryCommandIndeterm(const String& command); - bool queryCommandState(const String& command); - bool queryCommandSupported(const String& command); - String queryCommandValue(const String& command); + WEBCORE_EXPORT bool execCommand(const String& command, bool userInterface = false, const String& value = String()); + WEBCORE_EXPORT bool queryCommandEnabled(const String& command); + WEBCORE_EXPORT bool queryCommandIndeterm(const String& command); + WEBCORE_EXPORT bool queryCommandState(const String& command); + WEBCORE_EXPORT bool queryCommandSupported(const String& command); + WEBCORE_EXPORT String queryCommandValue(const String& command); // designMode support enum InheritedBool { off = false, on = true, inherit }; void setDesignMode(InheritedBool value); InheritedBool getDesignMode() const; bool inDesignMode() const; + WEBCORE_EXPORT String designMode() const; + WEBCORE_EXPORT void setDesignMode(const String&); Document* parentDocument() const; - Document& topDocument() const; + WEBCORE_EXPORT Document& topDocument() const; ScriptRunner* scriptRunner() { return m_scriptRunner.get(); } + ScriptModuleLoader* moduleLoader() { return m_moduleLoader.get(); } - HTMLScriptElement* currentScript() const { return !m_currentScriptStack.isEmpty() ? m_currentScriptStack.last().get() : 0; } - void pushCurrentScript(PassRefPtr<HTMLScriptElement>); + HTMLScriptElement* currentScript() const { return !m_currentScriptStack.isEmpty() ? m_currentScriptStack.last().get() : nullptr; } + void pushCurrentScript(HTMLScriptElement*); void popCurrentScript(); #if ENABLE(XSLT) void applyXSLTransform(ProcessingInstruction* pi); - PassRefPtr<Document> transformSourceDocument() { return m_transformSourceDocument; } + RefPtr<Document> transformSourceDocument() { return m_transformSourceDocument; } void setTransformSourceDocument(Document* doc) { m_transformSourceDocument = doc; } - void setTransformSource(PassOwnPtr<TransformSource>); + void setTransformSource(std::unique_ptr<TransformSource>); TransformSource* transformSource() const { return m_transformSource.get(); } #endif @@ -930,16 +932,9 @@ public: uint64_t domTreeVersion() const { return m_domTreeVersion; } // XPathEvaluator methods - PassRefPtr<XPathExpression> createExpression(const String& expression, - XPathNSResolver* resolver, - ExceptionCode& ec); - PassRefPtr<XPathNSResolver> createNSResolver(Node *nodeResolver); - PassRefPtr<XPathResult> evaluate(const String& expression, - Node* contextNode, - XPathNSResolver* resolver, - unsigned short type, - XPathResult* result, - ExceptionCode& ec); + WEBCORE_EXPORT ExceptionOr<Ref<XPathExpression>> createExpression(const String& expression, RefPtr<XPathNSResolver>&&); + WEBCORE_EXPORT Ref<XPathNSResolver> createNSResolver(Node* nodeResolver); + WEBCORE_EXPORT ExceptionOr<Ref<XPathResult>> evaluate(const String& expression, Node* contextNode, RefPtr<XPathNSResolver>&&, unsigned short type, XPathResult*); enum PendingSheetLayout { NoLayoutWithPendingSheets, DidLayoutWithPendingSheets, IgnoreLayoutWithPendingSheets }; @@ -948,15 +943,9 @@ public: bool hasNodesWithPlaceholderStyle() const { return m_hasNodesWithPlaceholderStyle; } void setHasNodesWithPlaceholderStyle() { m_hasNodesWithPlaceholderStyle = true; } - const Vector<IconURL>& shortcutIconURLs(); - const Vector<IconURL>& iconURLs(int iconTypesMask); - void addIconURL(const String& url, const String& mimeType, const String& size, IconType); - - void updateFocusAppearanceSoon(bool restorePreviousSelection); + void updateFocusAppearanceSoon(SelectionRestorationMode); void cancelFocusAppearanceUpdate(); - void resetHiddenFocusElementSoon(); - // Extension for manipulating canvas drawing contexts for use in CSS CanvasRenderingContext* getCSSCanvasContext(const String& type, const String& name, int width, int height); HTMLCanvasElement* getCSSCanvasElement(const String& name); @@ -964,32 +953,42 @@ public: bool isDNSPrefetchEnabled() const { return m_isDNSPrefetchEnabled; } void parseDNSPrefetchControlHeader(const String&); - virtual void postTask(PassOwnPtr<Task>) override; // Executes the task on context's thread asynchronously. + void postTask(Task&&) final; // Executes the task on context's thread asynchronously. + ScriptedAnimationController* scriptedAnimationController() { return m_scriptedAnimationController.get(); } void suspendScriptedAnimationControllerCallbacks(); void resumeScriptedAnimationControllerCallbacks(); - virtual void scriptedAnimationControllerSetThrottled(bool); + void scriptedAnimationControllerSetThrottled(bool); void windowScreenDidChange(PlatformDisplayID); void finishedParsing(); - bool inPageCache() const { return m_inPageCache; } - void setInPageCache(bool flag); + enum PageCacheState { NotInPageCache, AboutToEnterPageCache, InPageCache }; + + PageCacheState pageCacheState() const { return m_pageCacheState; } + void setPageCacheState(PageCacheState); - // Elements can register themselves for the "documentWillSuspendForPageCache()" and - // "documentDidResumeFromPageCache()" callbacks - void registerForPageCacheSuspensionCallbacks(Element*); - void unregisterForPageCacheSuspensionCallbacks(Element*); + // Elements can register themselves for the "suspend()" and + // "resume()" callbacks + void registerForDocumentSuspensionCallbacks(Element*); + void unregisterForDocumentSuspensionCallbacks(Element*); void documentWillBecomeInactive(); - void documentWillSuspendForPageCache(); - void documentDidResumeFromPageCache(); + void suspend(ActiveDOMObject::ReasonForSuspension); + void resume(ActiveDOMObject::ReasonForSuspension); void registerForMediaVolumeCallbacks(Element*); void unregisterForMediaVolumeCallbacks(Element*); void mediaVolumeDidChange(); + bool audioPlaybackRequiresUserGesture() const; + bool videoPlaybackRequiresUserGesture() const; + +#if ENABLE(MEDIA_SESSION) + MediaSession& defaultMediaSession(); +#endif + void registerForPrivateBrowsingStateChangedCallbacks(Element*); void unregisterForPrivateBrowsingStateChangedCallbacks(Element*); void storageBlockingStateDidChange(); @@ -1001,27 +1000,31 @@ public: void captionPreferencesChanged(); #endif -#if ENABLE(PAGE_VISIBILITY_API) +#if ENABLE(MEDIA_CONTROLS_SCRIPT) + void registerForPageScaleFactorChangedCallbacks(HTMLMediaElement*); + void unregisterForPageScaleFactorChangedCallbacks(HTMLMediaElement*); + void pageScaleFactorChangedAndStable(); + void registerForUserInterfaceLayoutDirectionChangedCallbacks(HTMLMediaElement&); + void unregisterForUserInterfaceLayoutDirectionChangedCallbacks(HTMLMediaElement&); + void userInterfaceLayoutDirectionChanged(); +#endif + void registerForVisibilityStateChangedCallbacks(Element*); void unregisterForVisibilityStateChangedCallbacks(Element*); + +#if ENABLE(VIDEO) + void registerForAllowsMediaDocumentInlinePlaybackChangedCallbacks(HTMLMediaElement&); + void unregisterForAllowsMediaDocumentInlinePlaybackChangedCallbacks(HTMLMediaElement&); + void allowsMediaDocumentInlinePlaybackChanged(); #endif - void setShouldCreateRenderers(bool); + WEBCORE_EXPORT void setShouldCreateRenderers(bool); bool shouldCreateRenderers(); - void setDecoder(PassRefPtr<TextResourceDecoder>); + void setDecoder(RefPtr<TextResourceDecoder>&&); TextResourceDecoder* decoder() const { return m_decoder.get(); } - String displayStringModifiedByEncoding(const String&) const; - PassRefPtr<StringImpl> displayStringModifiedByEncoding(PassRefPtr<StringImpl>) const; - void displayBufferModifiedByEncoding(LChar* buffer, unsigned len) const - { - displayBufferModifiedByEncodingInternal(buffer, len); - } - void displayBufferModifiedByEncoding(UChar* buffer, unsigned len) const - { - displayBufferModifiedByEncodingInternal(buffer, len); - } + WEBCORE_EXPORT String displayStringModifiedByEncoding(const String&) const; // Quirk for the benefit of Apple's Dictionary application. void setFrameElementsShouldIgnoreScrolling(bool ignore) { m_frameElementsShouldIgnoreScrolling = ignore; } @@ -1032,97 +1035,89 @@ public: bool annotatedRegionsDirty() const { return m_annotatedRegionsDirty; } bool hasAnnotatedRegions () const { return m_hasAnnotatedRegions; } void setHasAnnotatedRegions(bool f) { m_hasAnnotatedRegions = f; } - const Vector<AnnotatedRegionValue>& annotatedRegions() const; + WEBCORE_EXPORT const Vector<AnnotatedRegionValue>& annotatedRegions() const; void setAnnotatedRegions(const Vector<AnnotatedRegionValue>&); #endif - virtual void removeAllEventListeners() override; + void removeAllEventListeners() final; -#if ENABLE(SVG) - const SVGDocumentExtensions* svgExtensions(); - SVGDocumentExtensions* accessSVGExtensions(); -#endif + WEBCORE_EXPORT const SVGDocumentExtensions* svgExtensions(); + WEBCORE_EXPORT SVGDocumentExtensions& accessSVGExtensions(); void initSecurityContext(); void initContentSecurityPolicy(); void updateURLForPushOrReplaceState(const URL&); - void statePopped(PassRefPtr<SerializedScriptValue>); + void statePopped(Ref<SerializedScriptValue>&&); bool processingLoadEvent() const { return m_processingLoadEvent; } bool loadEventFinished() const { return m_loadEventFinished; } - virtual bool isContextThread() const override; - virtual bool isJSExecutionForbidden() const override { return false; } + bool isContextThread() const final; + bool isJSExecutionForbidden() const final { return false; } - bool containsValidityStyleRules() const { return m_containsValidityStyleRules; } - void setContainsValidityStyleRules() { m_containsValidityStyleRules = true; } - - void enqueueWindowEvent(PassRefPtr<Event>); - void enqueueDocumentEvent(PassRefPtr<Event>); - void enqueueOverflowEvent(PassRefPtr<Event>); - void enqueuePageshowEvent(PageshowEventPersistence); + void enqueueWindowEvent(Ref<Event>&&); + void enqueueDocumentEvent(Ref<Event>&&); + void enqueueOverflowEvent(Ref<Event>&&); + void dispatchPageshowEvent(PageshowEventPersistence); void enqueueHashchangeEvent(const String& oldURL, const String& newURL); - void enqueuePopstateEvent(PassRefPtr<SerializedScriptValue> stateObject); - virtual DocumentEventQueue& eventQueue() const override { return m_eventQueue; } + void dispatchPopstateEvent(RefPtr<SerializedScriptValue>&& stateObject); + DocumentEventQueue& eventQueue() const final { return m_eventQueue; } - void addMediaCanStartListener(MediaCanStartListener*); - void removeMediaCanStartListener(MediaCanStartListener*); + WEBCORE_EXPORT void addMediaCanStartListener(MediaCanStartListener*); + WEBCORE_EXPORT void removeMediaCanStartListener(MediaCanStartListener*); MediaCanStartListener* takeAnyMediaCanStartListener(); - const QualifiedName& idAttributeName() const { return m_idAttributeName; } - #if ENABLE(FULLSCREEN_API) bool webkitIsFullScreen() const { return m_fullScreenElement.get(); } bool webkitFullScreenKeyboardInputAllowed() const { return m_fullScreenElement.get() && m_areKeysEnabledInFullScreen; } Element* webkitCurrentFullScreenElement() const { return m_fullScreenElement.get(); } - + Element* webkitCurrentFullScreenElementForBindings() const { return ancestorElementInThisScope(webkitCurrentFullScreenElement()); } + enum FullScreenCheckType { EnforceIFrameAllowFullScreenRequirement, ExemptIFrameAllowFullScreenRequirement, }; - void requestFullScreenForElement(Element*, unsigned short flags, FullScreenCheckType); - void webkitCancelFullScreen(); + void requestFullScreenForElement(Element*, FullScreenCheckType); + WEBCORE_EXPORT void webkitCancelFullScreen(); - void webkitWillEnterFullScreenForElement(Element*); - void webkitDidEnterFullScreenForElement(Element*); - void webkitWillExitFullScreenForElement(Element*); - void webkitDidExitFullScreenForElement(Element*); + WEBCORE_EXPORT void webkitWillEnterFullScreenForElement(Element*); + WEBCORE_EXPORT void webkitDidEnterFullScreenForElement(Element*); + WEBCORE_EXPORT void webkitWillExitFullScreenForElement(Element*); + WEBCORE_EXPORT void webkitDidExitFullScreenForElement(Element*); void setFullScreenRenderer(RenderFullScreen*); RenderFullScreen* fullScreenRenderer() const { return m_fullScreenRenderer; } void fullScreenRendererDestroyed(); - void fullScreenChangeDelayTimerFired(Timer<Document>&); + void fullScreenChangeDelayTimerFired(); bool fullScreenIsAllowedForElement(Element*) const; void fullScreenElementRemoved(); - void removeFullScreenElementOfSubtree(Node*, bool amongChildrenOnly = false); + void removeFullScreenElementOfSubtree(Node&, bool amongChildrenOnly = false); bool isAnimatingFullScreen() const; - void setAnimatingFullScreen(bool); + WEBCORE_EXPORT void setAnimatingFullScreen(bool); - // W3C API - bool webkitFullscreenEnabled() const; - Element* webkitFullscreenElement() const { return !m_fullScreenElementStack.isEmpty() ? m_fullScreenElementStack.last().get() : 0; } - void webkitExitFullscreen(); + WEBCORE_EXPORT bool webkitFullscreenEnabled() const; + Element* webkitFullscreenElement() const { return !m_fullScreenElementStack.isEmpty() ? m_fullScreenElementStack.last().get() : nullptr; } + Element* webkitFullscreenElementForBindings() const { return ancestorElementInThisScope(webkitFullscreenElement()); } + WEBCORE_EXPORT void webkitExitFullscreen(); #endif #if ENABLE(POINTER_LOCK) - void webkitExitPointerLock(); - Element* webkitPointerLockElement() const; + WEBCORE_EXPORT void exitPointerLock(); #endif // Used to allow element that loads data without going through a FrameLoader to delay the 'load' event. void incrementLoadEventDelayCount() { ++m_loadEventDelayCount; } void decrementLoadEventDelayCount(); bool isDelayingLoadEvent() const { return m_loadEventDelayCount; } + void checkCompleted(); -#if ENABLE(TOUCH_EVENTS) -#if PLATFORM(IOS) +#if ENABLE(IOS_TOUCH_EVENTS) #include <WebKitAdditions/DocumentIOS.h> -#else - PassRefPtr<Touch> createTouch(DOMWindow*, EventTarget*, int identifier, int pageX, int pageY, int screenX, int screenY, int radiusX, int radiusY, float rotationAngle, float force, ExceptionCode&) const; -#endif // PLATFORM(IOS) +#elif ENABLE(TOUCH_EVENTS) + Ref<Touch> createTouch(DOMWindow*, EventTarget*, int identifier, int pageX, int pageY, int screenX, int screenY, int radiusX, int radiusY, float rotationAngle, float force) const; #endif #if ENABLE(DEVICE_ORIENTATION) && PLATFORM(IOS) @@ -1131,26 +1126,27 @@ public: #endif #if ENABLE(WEB_TIMING) - const DocumentTiming* timing() const { return &m_documentTiming; } + const DocumentTiming& timing() const { return m_documentTiming; } #endif -#if ENABLE(REQUEST_ANIMATION_FRAME) - int requestAnimationFrame(PassRefPtr<RequestAnimationFrameCallback>); + double monotonicTimestamp() const; + + int requestAnimationFrame(Ref<RequestAnimationFrameCallback>&&); void cancelAnimationFrame(int id); - void serviceScriptedAnimations(double monotonicAnimationStartTime); -#endif + void serviceScriptedAnimations(double timestamp); - virtual EventTarget* errorEventTarget() override; - virtual void logExceptionToConsole(const String& errorMessage, const String& sourceURL, int lineNumber, int columnNumber, PassRefPtr<ScriptCallStack>) override; + void sendWillRevealEdgeEventsIfNeeded(const IntPoint& oldPosition, const IntPoint& newPosition, const IntRect& visibleRect, const IntSize& contentsSize, Element* target = nullptr); + + EventTarget* errorEventTarget() final; + void logExceptionToConsole(const String& errorMessage, const String& sourceURL, int lineNumber, int columnNumber, RefPtr<Inspector::ScriptCallStack>&&) final; void initDNSPrefetch(); - unsigned wheelEventHandlerCount() const { return m_wheelEventHandlerCount; } - void didAddWheelEventHandler(); - void didRemoveWheelEventHandler(); + void didAddWheelEventHandler(Node&); + void didRemoveWheelEventHandler(Node&, EventHandlerRemoval = EventHandlerRemoval::One); double lastHandledUserGestureTimestamp() const { return m_lastHandledUserGestureTimestamp; } - void resetLastHandledUserGestureTimestamp(); + void updateLastHandledUserGestureTimestamp(); #if ENABLE(TOUCH_EVENTS) bool hasTouchEventHandlers() const { return (m_touchEventTargets.get()) ? m_touchEventTargets->size() : false; } @@ -1158,18 +1154,36 @@ public: bool hasTouchEventHandlers() const { return false; } #endif - void didAddTouchEventHandler(Node*); - void didRemoveTouchEventHandler(Node*); + void setUserDidInteractWithPage(bool userDidInteractWithPage) { ASSERT(&topDocument() == this); m_userDidInteractWithPage = userDidInteractWithPage; } + bool userDidInteractWithPage() const { ASSERT(&topDocument() == this); return m_userDidInteractWithPage; } -#if ENABLE(TOUCH_EVENTS) - void didRemoveEventTargetNode(Node*); -#endif + // Used for testing. Count handlers in the main document, and one per frame which contains handlers. + WEBCORE_EXPORT unsigned wheelEventHandlerCount() const; + WEBCORE_EXPORT unsigned touchEventHandlerCount() const; + + WEBCORE_EXPORT void startTrackingStyleRecalcs(); + WEBCORE_EXPORT unsigned styleRecalcCount() const; + + void didAddTouchEventHandler(Node&); + void didRemoveTouchEventHandler(Node&, EventHandlerRemoval = EventHandlerRemoval::One); + void didRemoveEventTargetNode(Node&); + + const EventTargetSet* touchEventTargets() const + { #if ENABLE(TOUCH_EVENTS) - const TouchEventTargetSet* touchEventTargets() const { return m_touchEventTargets.get(); } + return m_touchEventTargets.get(); #else - const TouchEventTargetSet* touchEventTargets() const { return 0; } + return nullptr; #endif + } + + const EventTargetSet* wheelEventTargets() const { return m_wheelEventTargets.get(); } + + typedef std::pair<Region, bool> RegionFixedPair; + RegionFixedPair absoluteRegionForEventTargets(const EventTargetSet*); + + LayoutRect absoluteEventHandlerBounds(bool&) final; bool visualUpdatesAllowed() const { return m_visualUpdatesAllowed; } @@ -1182,10 +1196,6 @@ public: IntSize initialViewportSize() const; #endif -#if ENABLE(TEXT_AUTOSIZING) - TextAutosizer* textAutosizer() { return m_textAutosizer.get(); } -#endif - void adjustFloatQuadsForScrollAndAbsoluteZoomAndFrameScale(Vector<FloatQuad>&, const RenderStyle&); void adjustFloatRectForScrollAndAbsoluteZoomAndFrameScale(FloatRect&, const RenderStyle&); @@ -1196,47 +1206,105 @@ public: DocumentSharedObjectPool* sharedObjectPool() { return m_sharedObjectPool.get(); } void didRemoveAllPendingStylesheet(); - void setNeedsNotifyRemoveAllPendingStylesheet() { m_needsNotifyRemoveAllPendingStylesheet = true; } - void clearStyleResolver(); - void notifySeamlessChildDocumentsOfStylesheetUpdate() const; + void didClearStyleResolver(); - bool inStyleRecalc() { return m_inStyleRecalc; } + bool inStyleRecalc() const { return m_inStyleRecalc; } + bool inRenderTreeUpdate() const { return m_inRenderTreeUpdate; } // Return a Locale for the default locale if the argument is null or empty. Locale& getCachedLocale(const AtomicString& locale = nullAtom); -#if ENABLE(TEMPLATE_ELEMENT) const Document* templateDocument() const; - Document* ensureTemplateDocument(); + Document& ensureTemplateDocument(); void setTemplateDocumentHost(Document* templateDocumentHost) { m_templateDocumentHost = templateDocumentHost; } Document* templateDocumentHost() { return m_templateDocumentHost; } -#endif void didAssociateFormControl(Element*); + bool hasDisabledFieldsetElement() const { return m_disabledFieldsetElementsCount; } + void addDisabledFieldsetElement() { m_disabledFieldsetElementsCount++; } + void removeDisabledFieldsetElement() { ASSERT(m_disabledFieldsetElementsCount); m_disabledFieldsetElementsCount--; } - virtual void addConsoleMessage(MessageSource, MessageLevel, const String& message, unsigned long requestIdentifier = 0) override; + WEBCORE_EXPORT void addConsoleMessage(MessageSource, MessageLevel, const String& message, unsigned long requestIdentifier = 0) final; - virtual SecurityOrigin* topOrigin() const override; + SecurityOrigin& securityOrigin() const { return *SecurityContext::securityOrigin(); } + SecurityOrigin& topOrigin() const final { return topDocument().securityOrigin(); } -#if ENABLE(FONT_LOAD_EVENTS) - PassRefPtr<FontLoader> fontloader(); -#endif + Ref<FontFaceSet> fonts(); void ensurePlugInsInjectedScript(DOMWrapperWorld&); void setVisualUpdatesAllowedByClient(bool); +#if ENABLE(SUBTLE_CRYPTO) + bool wrapCryptoKey(const Vector<uint8_t>& key, Vector<uint8_t>& wrappedKey) final; + bool unwrapCryptoKey(const Vector<uint8_t>& wrappedKey, Vector<uint8_t>& key) final; +#endif + + void setHasStyleWithViewportUnits() { m_hasStyleWithViewportUnits = true; } + bool hasStyleWithViewportUnits() const { return m_hasStyleWithViewportUnits; } + void updateViewportUnitsOnResize(); + + WEBCORE_EXPORT void addAudioProducer(MediaProducer*); + WEBCORE_EXPORT void removeAudioProducer(MediaProducer*); + MediaProducer::MediaStateFlags mediaState() const { return m_mediaState; } + WEBCORE_EXPORT void updateIsPlayingMedia(uint64_t = HTMLMediaElementInvalidID); + void pageMutedStateDidChange(); + WeakPtr<Document> createWeakPtr() { return m_weakFactory.createWeakPtr(); } + +#if ENABLE(WIRELESS_PLAYBACK_TARGET) + void addPlaybackTargetPickerClient(MediaPlaybackTargetClient&); + void removePlaybackTargetPickerClient(MediaPlaybackTargetClient&); + void showPlaybackTargetPicker(MediaPlaybackTargetClient&, bool); + void playbackTargetPickerClientStateDidChange(MediaPlaybackTargetClient&, MediaProducer::MediaStateFlags); + + void setPlaybackTarget(uint64_t, Ref<MediaPlaybackTarget>&&); + void playbackTargetAvailabilityDidChange(uint64_t, bool); + void setShouldPlayToPlaybackTarget(uint64_t, bool); +#endif + + ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicyToPropagate() const; + bool shouldEnforceContentDispositionAttachmentSandbox() const; + void applyContentDispositionAttachmentSandbox(); + + void addViewportDependentPicture(HTMLPictureElement&); + void removeViewportDependentPicture(HTMLPictureElement&); + +#if ENABLE(MEDIA_STREAM) + void setHasActiveMediaStreamTrack() { m_hasHadActiveMediaStreamTrack = true; } + bool hasHadActiveMediaStreamTrack() const { return m_hasHadActiveMediaStreamTrack; } +#endif + + using ContainerNode::setAttributeEventListener; + void setAttributeEventListener(const AtomicString& eventType, const QualifiedName& attributeName, const AtomicString& value, DOMWrapperWorld& isolatedWorld); + + DOMSelection* getSelection(); + + void didInsertInDocumentShadowRoot(ShadowRoot&); + void didRemoveInDocumentShadowRoot(ShadowRoot&); + const HashSet<ShadowRoot*>& inDocumentShadowRoots() const { return m_inDocumentShadowRoots; } + + void attachToCachedFrame(CachedFrameBase&); + void detachFromCachedFrame(CachedFrameBase&); + protected: enum ConstructionFlags { Synthesized = 1, NonRenderedPlaceholder = 1 << 1 }; Document(Frame*, const URL&, unsigned = DefaultDocumentClass, unsigned constructionFlags = 0); void clearXMLVersion() { m_xmlVersion = String(); } - virtual PassRefPtr<Document> cloneDocumentWithoutChildren() const; + virtual Ref<Document> cloneDocumentWithoutChildren() const; private: friend class Node; friend class IgnoreDestructiveWriteCountIncrementer; + friend class IgnoreOpensDuringUnloadCountIncrementer; + + bool shouldInheritContentSecurityPolicyFromOwner() const; + + void detachFromFrame() { observeFrame(nullptr); } + + void updateTitleElement(Element* newTitleElement); + void frameDestroyed() final; void commonTeardown(); @@ -1246,56 +1314,40 @@ private: void createRenderTree(); void detachParser(); - virtual void dropChildren() override; + // FontSelectorClient + void fontsNeedUpdate(FontSelector&) final; - typedef void (*ArgumentsCallback)(const String& keyString, const String& valueString, Document*, void* data); - void processArguments(const String& features, void* data, ArgumentsCallback); + bool isDocument() const final { return true; } - virtual bool isDocument() const override { return true; } + void childrenChanged(const ChildChange&) final; - virtual void childrenChanged(const ChildChange&) override; - - virtual String nodeName() const override; - virtual NodeType nodeType() const override; - virtual bool childTypeAllowed(NodeType) const override; - virtual PassRefPtr<Node> cloneNode(bool deep) override; + String nodeName() const final; + NodeType nodeType() const final; + bool childTypeAllowed(NodeType) const final; + Ref<Node> cloneNodeInternal(Document&, CloningOperation) final; void cloneDataFromDocument(const Document&); - virtual void refScriptExecutionContext() override { ref(); } - virtual void derefScriptExecutionContext() override { deref(); } + void refScriptExecutionContext() final { ref(); } + void derefScriptExecutionContext() final { deref(); } - virtual void addMessage(MessageSource, MessageLevel, const String& message, const String& sourceURL, unsigned lineNumber, unsigned columnNumber, PassRefPtr<ScriptCallStack>, JSC::ExecState* = 0, unsigned long requestIdentifier = 0) override; + void addMessage(MessageSource, MessageLevel, const String& message, const String& sourceURL, unsigned lineNumber, unsigned columnNumber, RefPtr<Inspector::ScriptCallStack>&&, JSC::ExecState* = nullptr, unsigned long requestIdentifier = 0) final; - virtual double minimumTimerInterval() const override; + std::chrono::milliseconds minimumTimerInterval() const final; - virtual double timerAlignmentInterval() const override; + std::chrono::milliseconds timerAlignmentInterval(bool hasReachedMaxNestingLevel) const final; + void updateTitleFromTitleElement(); void updateTitle(const StringWithDirection&); - void updateFocusAppearanceTimerFired(Timer<Document>&); + void updateFocusAppearanceTimerFired(); void updateBaseURL(); - void resetHiddenFocusElementTimer(Timer<Document>&); - void buildAccessKeyMap(TreeScope* root); - void createStyleResolver(); - - void seamlessParentUpdatedStylesheets(); + void loadEventDelayTimerFired(); - void loadEventDelayTimerFired(Timer<Document>&); + void pendingTasksTimerFired(); - void pendingTasksTimerFired(Timer<Document>&); - - static void didReceiveTask(void*); - - template <typename CharacterType> - void displayBufferModifiedByEncodingInternal(CharacterType*, unsigned) const; - -#if ENABLE(PAGE_VISIBILITY_API) - PageVisibilityState pageVisibilityState() const; -#endif - - PassRefPtr<HTMLCollection> ensureCachedCollection(CollectionType); + template<CollectionType> Ref<HTMLCollection> ensureCachedCollection(); #if ENABLE(FULLSCREEN_API) void dispatchFullScreenChangeOrErrorEvent(Deque<RefPtr<Node>>&, const AtomicString& eventName, bool shouldNotifyMediaElement); @@ -1307,19 +1359,39 @@ private: void setVisualUpdatesAllowed(ReadyState); void setVisualUpdatesAllowed(bool); - void visualUpdatesSuppressionTimerFired(Timer<Document>&); + void visualUpdatesSuppressionTimerFired(); void addListenerType(ListenerType listenerType) { m_listenerTypes |= listenerType; } - void didAssociateFormControlsTimerFired(Timer<Document>&); + void didAssociateFormControlsTimerFired(); + + void wheelEventHandlersChanged(); + + HttpEquivPolicy httpEquivPolicy() const; + AXObjectCache* existingAXObjectCacheSlow() const; - void styleResolverThrowawayTimerFired(DeferrableOneShotTimer<Document>&); - DeferrableOneShotTimer<Document> m_styleResolverThrowawayTimer; + // DOM Cookies caching. + const String& cachedDOMCookies() const { return m_cachedDOMCookies; } + void setCachedDOMCookies(const String&); + bool isDOMCookieCacheValid() const { return m_cookieCacheExpiryTimer.isActive(); } + void invalidateDOMCookieCache(); + void didLoadResourceSynchronously() final; - OwnPtr<StyleResolver> m_styleResolver; - bool m_didCalculateStyleResolver; + void checkViewportDependentPictures(); + +#if USE(QUICK_LOOK) + bool shouldEnforceQuickLookSandbox() const; + void applyQuickLookSandbox(); +#endif + + bool shouldEnforceHTTP09Sandbox() const; + + unsigned m_referencingNodeCount; + + const Ref<Settings> m_settings; + + std::unique_ptr<StyleResolver> m_userAgentShadowTreeStyleResolver; bool m_hasNodesWithPlaceholderStyle; - bool m_needsNotifyRemoveAllPendingStylesheet; // But sometimes you need to ignore pending stylesheet count to // force an immediate layout when requested by JS. bool m_ignorePendingStylesheets; @@ -1329,10 +1401,10 @@ private: // do eventually load. PendingSheetLayout m_pendingSheetLayout; - Frame* m_frame; RefPtr<DOMWindow> m_domWindow; + WeakPtr<Document> m_contextDocument; - RefPtr<CachedResourceLoader> m_cachedResourceLoader; + Ref<CachedResourceLoader> m_cachedResourceLoader; RefPtr<DocumentParser> m_parser; unsigned m_activeParserCount; @@ -1357,20 +1429,23 @@ private: String m_baseTarget; - OwnPtr<DOMImplementation> m_implementation; + // MIME type of the document in case it was cloned or created by XHR. + String m_overriddenMIMEType; - RefPtr<CSSStyleSheet> m_elementSheet; + std::unique_ptr<DOMImplementation> m_implementation; + + bool m_hasElementUsingStyleBasedEditability { false }; bool m_printing; bool m_paginatedForScreen; - bool m_ignoreAutofocus; - - CompatibilityMode m_compatibilityMode; + DocumentCompatibilityMode m_compatibilityMode; bool m_compatibilityModeLocked; // This is cheaper than making setCompatibilityMode virtual. Color m_textColor; + bool m_focusNavigationStartingNodeIsRemoved; + RefPtr<Node> m_focusNavigationStartingNode; RefPtr<Element> m_focusedElement; RefPtr<Element> m_hoveredElement; RefPtr<Element> m_activeElement; @@ -1383,70 +1458,75 @@ private: HashSet<NodeIterator*> m_nodeIterators; HashSet<Range*> m_ranges; - unsigned short m_listenerTypes; + unsigned m_listenerTypes; MutationObserverOptions m_mutationObserverTypes; - DocumentStyleSheetCollection m_styleSheetCollection; + std::unique_ptr<Style::Scope> m_styleScope; + std::unique_ptr<ExtensionStyleSheets> m_extensionStyleSheets; RefPtr<StyleSheetList> m_styleSheetList; - OwnPtr<FormController> m_formController; + std::unique_ptr<FormController> m_formController; Color m_linkColor; Color m_visitedLinkColor; Color m_activeLinkColor; - const OwnPtr<VisitedLinkState> m_visitedLinkState; + const std::unique_ptr<VisitedLinkState> m_visitedLinkState; bool m_visuallyOrdered; ReadyState m_readyState; bool m_bParsing; - Timer<Document> m_optimizedStyleSheetUpdateTimer; - Timer<Document> m_styleRecalcTimer; + Timer m_styleRecalcTimer; bool m_pendingStyleRecalcShouldForce; bool m_inStyleRecalc; bool m_closeAfterStyleRecalc; + bool m_inRenderTreeUpdate { false }; + unsigned m_lastStyleUpdateSizeForTesting { 0 }; bool m_gotoAnchorNeededAfterStylesheetsLoad; bool m_isDNSPrefetchEnabled; bool m_haveExplicitlyDisabledDNSPrefetch; bool m_frameElementsShouldIgnoreScrolling; - bool m_containsValidityStyleRules; - bool m_updateFocusAppearanceRestoresSelection; + SelectionRestorationMode m_updateFocusAppearanceRestoresSelection; - // http://www.whatwg.org/specs/web-apps/current-work/#ignore-destructive-writes-counter - unsigned m_ignoreDestructiveWriteCount; + // https://html.spec.whatwg.org/multipage/webappapis.html#ignore-destructive-writes-counter + unsigned m_ignoreDestructiveWriteCount { 0 }; + + // https://html.spec.whatwg.org/multipage/webappapis.html#ignore-opens-during-unload-counter + unsigned m_ignoreOpensDuringUnloadCount { 0 }; + + unsigned m_styleRecalcCount { 0 }; StringWithDirection m_title; StringWithDirection m_rawTitle; - bool m_titleSetExplicitly; RefPtr<Element> m_titleElement; - OwnPtr<AXObjectCache> m_axObjectCache; - const OwnPtr<DocumentMarkerController> m_markers; + std::unique_ptr<AXObjectCache> m_axObjectCache; + const std::unique_ptr<DocumentMarkerController> m_markers; - Timer<Document> m_updateFocusAppearanceTimer; - Timer<Document> m_resetHiddenFocusElementTimer; + Timer m_updateFocusAppearanceTimer; Element* m_cssTarget; // FIXME: Merge these 2 variables into an enum. Also, FrameLoader::m_didCallImplicitClose // is almost a duplication of this data, so that should probably get merged in too. - // FIXME: Document::m_processingLoadEvent and DocumentLoader::m_wasOnloadHandled are roughly the same + // FIXME: Document::m_processingLoadEvent and DocumentLoader::m_wasOnloadDispatched are roughly the same // and should be merged. bool m_processingLoadEvent; bool m_loadEventFinished; RefPtr<SerializedScriptValue> m_pendingStateObject; - std::chrono::steady_clock::time_point m_startTime; + MonotonicTime m_documentCreationTime; bool m_overMinimumLayoutThreshold; std::unique_ptr<ScriptRunner> m_scriptRunner; + std::unique_ptr<ScriptModuleLoader> m_moduleLoader; Vector<RefPtr<HTMLScriptElement>> m_currentScriptStack; #if ENABLE(XSLT) - OwnPtr<TransformSource> m_transformSource; + std::unique_ptr<TransformSource> m_transformSource; RefPtr<Document> m_transformSourceDocument; #endif @@ -1463,14 +1543,11 @@ private: HashSet<LiveNodeList*> m_listsInvalidatedAtDocument; HashSet<HTMLCollection*> m_collectionsInvalidatedAtDocument; - unsigned m_nodeListAndCollectionCounts[numNodeListInvalidationTypes]; RefPtr<XPathEvaluator> m_xpathEvaluator; -#if ENABLE(SVG) - OwnPtr<SVGDocumentExtensions> m_svgExtensions; -#endif + std::unique_ptr<SVGDocumentExtensions> m_svgExtensions; #if ENABLE(DASHBOARD_SUPPORT) Vector<AnnotatedRegionValue> m_annotatedRegions; @@ -1481,8 +1558,7 @@ private: HashMap<String, RefPtr<HTMLCanvasElement>> m_cssCanvasElements; bool m_createRenderers; - bool m_inPageCache; - Vector<IconURL> m_iconURLs; + PageCacheState m_pageCacheState { NotInPageCache }; HashSet<Element*> m_documentSuspensionCallbackElements; HashSet<Element*> m_mediaVolumeCallbackElements; @@ -1491,23 +1567,28 @@ private: HashSet<Element*> m_captionPreferencesChangedElements; #endif -#if ENABLE(PAGE_VISIBILITY_API) +#if ENABLE(MEDIA_CONTROLS_SCRIPT) + HashSet<HTMLMediaElement*> m_pageScaleFactorChangedElements; + HashSet<HTMLMediaElement*> m_userInterfaceLayoutDirectionChangedElements; +#endif + HashSet<Element*> m_visibilityStateCallbackElements; +#if ENABLE(VIDEO) + HashSet<HTMLMediaElement*> m_allowsMediaDocumentInlinePlaybackElements; #endif - HashMap<StringImpl*, Element*, CaseFoldingHash> m_elementsByAccessKey; + HashMap<StringImpl*, Element*, ASCIICaseInsensitiveHash> m_elementsByAccessKey; bool m_accessKeyMapValid; DocumentOrderedMap m_imagesByUsemap; - OwnPtr<SelectorQueryCache> m_selectorQueryCache; + std::unique_ptr<SelectorQueryCache> m_selectorQueryCache; DocumentClassFlags m_documentClasses; bool m_isSynthesized; bool m_isNonRenderedPlaceholder; - bool m_isViewSource; bool m_sawElementsInKnownNamespaces; bool m_isSrcdocDocument; @@ -1518,31 +1599,28 @@ private: HashSet<MediaCanStartListener*> m_mediaCanStartListeners; - QualifiedName m_idAttributeName; - #if ENABLE(FULLSCREEN_API) bool m_areKeysEnabledInFullScreen; RefPtr<Element> m_fullScreenElement; Vector<RefPtr<Element>> m_fullScreenElementStack; RenderFullScreen* m_fullScreenRenderer; - Timer<Document> m_fullScreenChangeDelayTimer; + Timer m_fullScreenChangeDelayTimer; Deque<RefPtr<Node>> m_fullScreenChangeEventTargetQueue; Deque<RefPtr<Node>> m_fullScreenErrorEventTargetQueue; bool m_isAnimatingFullScreen; LayoutRect m_savedPlaceholderFrameRect; - RefPtr<RenderStyle> m_savedPlaceholderRenderStyle; + std::unique_ptr<RenderStyle> m_savedPlaceholderRenderStyle; #endif + HashSet<HTMLPictureElement*> m_viewportDependentPictures; + int m_loadEventDelayCount; - Timer<Document> m_loadEventDelayTimer; + Timer m_loadEventDelayTimer; ViewportArguments m_viewportArguments; ReferrerPolicy m_referrerPolicy; - bool m_directionSetOnDocumentElement; - bool m_writingModeSetOnDocumentElement; - #if ENABLE(WEB_TIMING) DocumentTiming m_documentTiming; #endif @@ -1551,28 +1629,25 @@ private: bool m_writeRecursionIsTooDeep; unsigned m_writeRecursionDepth; - unsigned m_wheelEventHandlerCount; #if ENABLE(TOUCH_EVENTS) - OwnPtr<TouchEventTargetSet> m_touchEventTargets; + std::unique_ptr<EventTargetSet> m_touchEventTargets; #endif + std::unique_ptr<EventTargetSet> m_wheelEventTargets; double m_lastHandledUserGestureTimestamp; -#if ENABLE(REQUEST_ANIMATION_FRAME) void clearScriptedAnimationController(); RefPtr<ScriptedAnimationController> m_scriptedAnimationController; -#endif #if ENABLE(DEVICE_ORIENTATION) && PLATFORM(IOS) - // FIXME: Use std::unique_ptr instead of OwnPtr after we upstream DeviceMotionClientIOS.{h, mm}. - OwnPtr<DeviceMotionClient> m_deviceMotionClient; - OwnPtr<DeviceMotionController> m_deviceMotionController; - OwnPtr<DeviceOrientationClient> m_deviceOrientationClient; - OwnPtr<DeviceOrientationController> m_deviceOrientationController; + std::unique_ptr<DeviceMotionClient> m_deviceMotionClient; + std::unique_ptr<DeviceMotionController> m_deviceMotionController; + std::unique_ptr<DeviceOrientationClient> m_deviceOrientationClient; + std::unique_ptr<DeviceOrientationController> m_deviceOrientationController; #endif // FIXME: Find a better place for this functionality. -#if PLATFORM(IOS) +#if ENABLE(TELEPHONE_NUMBER_DETECTION) public: // These functions provide a two-level setting: @@ -1581,8 +1656,8 @@ public: // document if it has the appropriate meta tag. // - isTelephoneNumberParsingEnabled() == isTelephoneNumberParsingAllowed() && page()->settings()->isTelephoneNumberParsingEnabled() - bool isTelephoneNumberParsingAllowed() const; - bool isTelephoneNumberParsingEnabled() const; + WEBCORE_EXPORT bool isTelephoneNumberParsingAllowed() const; + WEBCORE_EXPORT bool isTelephoneNumberParsingEnabled() const; private: friend void setParserFeature(const String& key, const String& value, Document*, void* userData); @@ -1591,148 +1666,132 @@ private: bool m_isTelephoneNumberParsingAllowed; #endif - Timer<Document> m_pendingTasksTimer; - Vector<OwnPtr<Task>> m_pendingTasks; + Timer m_pendingTasksTimer; + Vector<Task> m_pendingTasks; -#if ENABLE(IOS_TEXT_AUTOSIZING) +#if ENABLE(TEXT_AUTOSIZING) public: - void addAutoSizingNode(Node*, float size); - void validateAutoSizingNodes(); - void resetAutoSizingNodes(); + void addAutoSizedNode(Text&, float size); + void updateAutoSizedNodes(); + void clearAutoSizedNodes(); private: - typedef HashMap<TextAutoSizingKey, RefPtr<TextAutoSizingValue>, TextAutoSizingHash, TextAutoSizingTraits> TextAutoSizingMap; + using TextAutoSizingMap = HashMap<TextAutoSizingKey, std::unique_ptr<TextAutoSizingValue>, TextAutoSizingHash, TextAutoSizingTraits>; TextAutoSizingMap m_textAutoSizedNodes; #endif -#if ENABLE(TEXT_AUTOSIZING) - OwnPtr<TextAutosizer> m_textAutosizer; -#endif - void platformSuspendOrStopActiveDOMObjects(); bool m_scheduledTasksAreSuspended; bool m_visualUpdatesAllowed; - Timer<Document> m_visualUpdatesSuppressionTimer; + Timer m_visualUpdatesSuppressionTimer; RefPtr<NamedFlowCollection> m_namedFlows; -#if ENABLE(CSP_NEXT) - RefPtr<DOMSecurityPolicy> m_domSecurityPolicy; -#endif - - void sharedObjectPoolClearTimerFired(Timer<Document>&); - Timer<Document> m_sharedObjectPoolClearTimer; + void clearSharedObjectPool(); + Timer m_sharedObjectPoolClearTimer; - OwnPtr<DocumentSharedObjectPool> m_sharedObjectPool; + std::unique_ptr<DocumentSharedObjectPool> m_sharedObjectPool; #ifndef NDEBUG bool m_didDispatchViewportPropertiesChanged; #endif - typedef HashMap<AtomicString, OwnPtr<Locale>> LocaleIdentifierToLocaleMap; + typedef HashMap<AtomicString, std::unique_ptr<Locale>> LocaleIdentifierToLocaleMap; LocaleIdentifierToLocaleMap m_localeCache; -#if ENABLE(TEMPLATE_ELEMENT) RefPtr<Document> m_templateDocument; Document* m_templateDocumentHost; // Manually managed weakref (backpointer from m_templateDocument). -#endif -#if ENABLE(FONT_LOAD_EVENTS) - RefPtr<FontLoader> m_fontloader; + Ref<CSSFontSelector> m_fontSelector; + +#if ENABLE(WEB_REPLAY) + Ref<JSC::InputCursor> m_inputCursor; #endif - Timer<Document> m_didAssociateFormControlsTimer; + Timer m_didAssociateFormControlsTimer; + Timer m_cookieCacheExpiryTimer; + String m_cachedDOMCookies; HashSet<RefPtr<Element>> m_associatedFormControls; + unsigned m_disabledFieldsetElementsCount; bool m_hasInjectedPlugInsScript; - bool m_renderTreeBeingDestroyed; -}; - -inline void Document::notifyRemovePendingSheetIfNeeded() -{ - if (m_needsNotifyRemoveAllPendingStylesheet) - didRemoveAllPendingStylesheet(); -} + bool m_renderTreeBeingDestroyed { false }; + bool m_hasPreparedForDestruction { false }; -#if ENABLE(TEMPLATE_ELEMENT) -inline const Document* Document::templateDocument() const -{ - // If DOCUMENT does not have a browsing context, Let TEMPLATE CONTENTS OWNER be DOCUMENT and abort these steps. - if (!m_frame) - return this; + bool m_hasStyleWithViewportUnits; + bool m_isTimerThrottlingEnabled { false }; + bool m_isSuspended { false }; - return m_templateDocument.get(); -} -#endif + HashSet<MediaProducer*> m_audioProducers; + MediaProducer::MediaStateFlags m_mediaState { MediaProducer::IsNotPlaying }; -// Put these methods here, because they require the Document definition, but we really want to inline them. + HashSet<ShadowRoot*> m_inDocumentShadowRoots; -inline bool Node::isDocumentNode() const -{ - return this == documentInternal(); -} +#if ENABLE(WIRELESS_PLAYBACK_TARGET) + typedef HashMap<uint64_t, WebCore::MediaPlaybackTargetClient*> TargetIdToClientMap; + TargetIdToClientMap m_idToClientMap; + typedef HashMap<WebCore::MediaPlaybackTargetClient*, uint64_t> TargetClientToIdMap; + TargetClientToIdMap m_clientToIDMap; +#endif -inline Node::Node(Document* document, ConstructionType type) - : m_nodeFlags(type) - , m_parentNode(0) - , m_treeScope(document ? document : &TreeScope::noDocumentInstance()) - , m_previous(0) - , m_next(0) -{ - m_treeScope->selfOnlyRef(); +#if ENABLE(MEDIA_SESSION) + RefPtr<MediaSession> m_defaultMediaSession; +#endif + bool m_areDeviceMotionAndOrientationUpdatesSuspended { false }; + bool m_userDidInteractWithPage { false }; -#if !defined(NDEBUG) || (defined(DUMP_NODE_STATISTICS) && DUMP_NODE_STATISTICS) - trackForDebugging(); +#if ENABLE(MEDIA_STREAM) + bool m_hasHadActiveMediaStreamTrack { false }; #endif - InspectorCounters::incrementCounter(InspectorCounters::NodeCounter); -} +#if ENABLE(INDEXED_DATABASE) + RefPtr<IDBClient::IDBConnectionProxy> m_idbConnectionProxy; +#endif +#if ENABLE(WEB_SOCKETS) + RefPtr<SocketProvider> m_socketProvider; +#endif -inline ScriptExecutionContext* Node::scriptExecutionContext() const -{ - return &document(); -} + static bool hasEverCreatedAnAXObjectCache; +}; Element* eventTargetElementForDocument(Document*); -inline Document& toDocument(ScriptExecutionContext& scriptExecutionContext) +inline TextEncoding Document::textEncoding() const { - ASSERT_WITH_SECURITY_IMPLICATION(scriptExecutionContext.isDocument()); - return static_cast<Document&>(scriptExecutionContext); + if (auto* decoder = this->decoder()) + return decoder->encoding(); + return TextEncoding(); } -inline const Document& toDocument(const ScriptExecutionContext& scriptExecutionContext) +inline const Document* Document::templateDocument() const { - ASSERT_WITH_SECURITY_IMPLICATION(scriptExecutionContext.isDocument()); - return static_cast<const Document&>(scriptExecutionContext); + return m_templateDocumentHost ? this : m_templateDocument.get(); } -inline Document* toDocument(ScriptExecutionContext* scriptExecutionContext) +inline AXObjectCache* Document::existingAXObjectCache() const { - ASSERT_WITH_SECURITY_IMPLICATION(!scriptExecutionContext || scriptExecutionContext->isDocument()); - return static_cast<Document*>(scriptExecutionContext); + if (!hasEverCreatedAnAXObjectCache) + return nullptr; + return existingAXObjectCacheSlow(); } -inline const Document* toDocument(const ScriptExecutionContext* scriptExecutionContext) +// These functions are here because they require the Document class definition and we want to inline them. + +inline bool Node::isDocumentNode() const { - ASSERT_WITH_SECURITY_IMPLICATION(!scriptExecutionContext || scriptExecutionContext->isDocument()); - return static_cast<const Document*>(scriptExecutionContext); + return this == &document(); } -inline bool isDocument(const Node& node) { return node.isDocumentNode(); } -void isDocument(const Document&); // Catch unnecessary runtime check of type known at compile time. - -NODE_TYPE_CASTS(Document) - -#define DOCUMENT_TYPE_CASTS(ToClassName) \ - TYPE_CASTS_BASE(ToClassName, Document, document, WebCore::is##ToClassName(*document), WebCore::is##ToClassName(document)) +inline ScriptExecutionContext* Node::scriptExecutionContext() const +{ + return &document().contextDocument(); +} } // namespace WebCore -namespace WTF { -inline WebCore::Document* getPtr(WebCore::Document& p) { return &p; } -} - -#endif // Document_h +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::Document) + static bool isType(const WebCore::ScriptExecutionContext& context) { return context.isDocument(); } + static bool isType(const WebCore::Node& node) { return node.isDocumentNode(); } +SPECIALIZE_TYPE_TRAITS_END() diff --git a/Source/WebCore/dom/Document.idl b/Source/WebCore/dom/Document.idl index e84b586ee..34fa78039 100644 --- a/Source/WebCore/dom/Document.idl +++ b/Source/WebCore/dom/Document.idl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2011, 2015 Apple Inc. All rights reserved. * Copyright (C) 2006, 2007 Samuel Weinig <sam@webkit.org> * * This library is free software; you can redistribute it and/or @@ -19,340 +19,199 @@ */ [ + Constructor, + ConstructorCallWith=Document, CustomToJSObject, + ExportMacro=WEBCORE_EXPORT, + JSCustomHeader, + JSCustomMarkFunction, JSGenerateToNativeObject, ] interface Document : Node { - - // DOM Level 1 Core - readonly attribute DocumentType doctype; + readonly attribute DocumentType? doctype; readonly attribute DOMImplementation implementation; - readonly attribute Element documentElement; - - [ReturnNewObject, RaisesException] Element createElement([TreatNullAs=NullString,Default=Undefined] optional DOMString tagName); - DocumentFragment createDocumentFragment(); - [ReturnNewObject] Text createTextNode([Default=Undefined] optional DOMString data); - [ReturnNewObject] Comment createComment([Default=Undefined] optional DOMString data); - [ReturnNewObject, RaisesException] CDATASection createCDATASection([Default=Undefined] optional DOMString data); - [ObjCLegacyUnnamedParameters, ReturnNewObject, RaisesException] ProcessingInstruction createProcessingInstruction([Default=Undefined] optional DOMString target, - [Default=Undefined] optional DOMString data); - [ReturnNewObject, RaisesException] Attr createAttribute([Default=Undefined] optional DOMString name); - [ReturnNewObject, RaisesException] EntityReference createEntityReference([Default=Undefined] optional DOMString name); - NodeList getElementsByTagName([Default=Undefined] optional DOMString tagname); - - // Introduced in DOM Level 2: - - [ObjCLegacyUnnamedParameters, ReturnNewObject, RaisesException] Node importNode([Default=Undefined] optional Node importedNode, - optional boolean deep); - [ObjCLegacyUnnamedParameters, ReturnNewObject, RaisesException] Element createElementNS([TreatNullAs=NullString,Default=Undefined] optional DOMString namespaceURI, - [TreatNullAs=NullString,Default=Undefined] optional DOMString qualifiedName); - [ObjCLegacyUnnamedParameters, ReturnNewObject, RaisesException] Attr createAttributeNS([TreatNullAs=NullString,Default=Undefined] optional DOMString namespaceURI, - [TreatNullAs=NullString,Default=Undefined] optional DOMString qualifiedName); - [ObjCLegacyUnnamedParameters] NodeList getElementsByTagNameNS([TreatNullAs=NullString,Default=Undefined] optional DOMString namespaceURI, - [Default=Undefined] optional DOMString localName); - Element getElementById([Default=Undefined] optional DOMString elementId); - - // DOM Level 3 Core - - [TreatReturnedNullStringAs=Null] readonly attribute DOMString inputEncoding; - - [TreatReturnedNullStringAs=Null] readonly attribute DOMString xmlEncoding; - [TreatReturnedNullStringAs=Null, TreatNullAs=NullString, SetterRaisesException] attribute DOMString xmlVersion; - [SetterRaisesException] attribute boolean xmlStandalone; - - [RaisesException] Node adoptNode([Default=Undefined] optional Node source); - -#if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C || defined(LANGUAGE_GOBJECT) && LANGUAGE_GOBJECT - // document.documentURI was writable in DOM3 Core, but is read-only in DOM4 - // (see http://www.w3.org/TR/2011/WD-dom-20110915/#document). We need to keep - // the writable version around for Objective C clients, but are moving to - // read-only for other clients. - [TreatReturnedNullStringAs=Null, TreatNullAs=NullString] attribute DOMString documentURI; -#else - [TreatReturnedNullStringAs=Null] readonly attribute DOMString documentURI; -#endif + [DOMJIT=Getter] readonly attribute Element? documentElement; - // DOM Level 2 Events (DocumentEvents interface) + [NewObject, MayThrowException, ImplementedAs=createElementForBindings] Element createElement(DOMString tagName); + [NewObject] DocumentFragment createDocumentFragment(); - [RaisesException] Event createEvent([Default=Undefined] optional DOMString eventType); + [NewObject] Text createTextNode(DOMString data); + [NewObject] Comment createComment(DOMString data); + [NewObject, MayThrowException] CDATASection createCDATASection(DOMString data); + [NewObject, MayThrowException] ProcessingInstruction createProcessingInstruction(DOMString target, DOMString data); + [NewObject, MayThrowException] Attr createAttribute(DOMString name); - // DOM Level 2 Tranversal and Range (DocumentRange interface) + HTMLCollection getElementsByTagName(DOMString tagname); - Range createRange(); + [CEReactions, MayThrowException, NewObject] Node importNode(Node importedNode, optional boolean deep = false); - // DOM Level 2 Tranversal and Range (DocumentTraversal interface) + [NewObject, MayThrowException] Element createElementNS(DOMString? namespaceURI, DOMString qualifiedName); + [NewObject, MayThrowException] Attr createAttributeNS(DOMString? namespaceURI, DOMString qualifiedName); - [ObjCLegacyUnnamedParameters, RaisesException] NodeIterator createNodeIterator([Default=Undefined] optional Node root, - [Default=Undefined] optional unsigned long whatToShow, - [Default=Undefined] optional NodeFilter filter, - [Default=Undefined] optional boolean expandEntityReferences); - [ObjCLegacyUnnamedParameters, RaisesException] TreeWalker createTreeWalker([Default=Undefined] optional Node root, - [Default=Undefined] optional unsigned long whatToShow, - [Default=Undefined] optional NodeFilter filter, - [Default=Undefined] optional boolean expandEntityReferences); + HTMLCollection getElementsByTagNameNS(DOMString? namespaceURI, DOMString localName); - // DOM Level 2 Abstract Views (DocumentView interface) + [ImplementedAs=characterSetWithUTF8Fallback] readonly attribute DOMString inputEncoding; - readonly attribute DOMWindow defaultView; + readonly attribute DOMString? xmlEncoding; + [SetterMayThrowException] attribute DOMString? xmlVersion; + attribute boolean xmlStandalone; + + [CEReactions, MayThrowException] Node adoptNode(Node source); + + [ImplementedAs=urlForBindings] readonly attribute USVString documentURI; - // DOM Level 2 Style (DocumentStyle interface) + [MayThrowException, NewObject] Event createEvent(DOMString eventType); + + [NewObject] Range createRange(); + + [NewObject] NodeIterator createNodeIterator(Node root, optional unsigned long whatToShow = 0xFFFFFFFF, optional NodeFilter? filter = null); + [NewObject] TreeWalker createTreeWalker(Node root, optional unsigned long whatToShow = 0xFFFFFFFF, optional NodeFilter? filter = null); + + readonly attribute DOMWindow defaultView; readonly attribute StyleSheetList styleSheets; - // DOM Level 2 Style (DocumentCSS interface) - - [ObjCLegacyUnnamedParameters] CSSStyleDeclaration getOverrideStyle([Default=Undefined] optional Element element, - [Default=Undefined] optional DOMString pseudoElement); - - // DOM Level 3 XPath (XPathEvaluator interface) - [ObjCLegacyUnnamedParameters, RaisesException] XPathExpression createExpression([Default=Undefined] optional DOMString expression, - [Default=Undefined] optional XPathNSResolver resolver); - XPathNSResolver createNSResolver(Node nodeResolver); - [ObjCLegacyUnnamedParameters, RaisesException] XPathResult evaluate([Default=Undefined] optional DOMString expression, - [Default=Undefined] optional Node contextNode, - [Default=Undefined] optional XPathNSResolver resolver, - [Default=Undefined] optional unsigned short type, - [Default=Undefined] optional XPathResult inResult); - - // Common extensions - boolean execCommand([Default=Undefined] optional DOMString command, - [Default=Undefined] optional boolean userInterface, - [TreatNullAs=NullString, TreatUndefinedAs=NullString, Default=Undefined] optional DOMString value); - -#if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C - // FIXME: remove the these two versions once optional is implemented for Objective-C. - boolean execCommand(DOMString command, - boolean userInterface); - boolean execCommand(DOMString command); -#endif + // FIXME: Using "undefined" as default parameter value is wrong. + CSSStyleDeclaration getOverrideStyle(optional Element? element = null, optional DOMString pseudoElement = "undefined"); - boolean queryCommandEnabled([Default=Undefined] optional DOMString command); - boolean queryCommandIndeterm([Default=Undefined] optional DOMString command); - boolean queryCommandState([Default=Undefined] optional DOMString command); - boolean queryCommandSupported([Default=Undefined] optional DOMString command); - DOMString queryCommandValue([Default=Undefined] optional DOMString command); + readonly attribute DOMString contentType; - // Moved down from HTMLDocument + // FIXME: Using "undefined" as default parameter value is wrong. + [MayThrowException] XPathExpression createExpression(optional DOMString expression = "undefined", optional XPathNSResolver? resolver); + XPathNSResolver createNSResolver(Node? nodeResolver); - [TreatNullAs=NullString] attribute DOMString title; - readonly attribute DOMString referrer; -#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT - [TreatNullAs=NullString, SetterRaisesException] attribute DOMString domain; -#else - readonly attribute DOMString domain; -#endif - readonly attribute DOMString URL; + // FIXME: Using "undefined" as default parameter value is wrong. + [MayThrowException] XPathResult evaluate(optional DOMString expression = "undefined", optional Node? contextNode, optional XPathNSResolver? resolver, optional unsigned short type = 0, optional XPathResult? inResult); + + // FIXME: The default value for the last parameter should be the empty string. + [CEReactions] boolean execCommand(DOMString command, optional boolean userInterface = false, optional DOMString? value = null); + + boolean queryCommandEnabled(DOMString command); + boolean queryCommandIndeterm(DOMString command); + boolean queryCommandState(DOMString command); + boolean queryCommandSupported(DOMString command); + DOMString queryCommandValue(DOMString command); - [TreatNullAs=NullString, GetterRaisesException, SetterRaisesException] attribute DOMString cookie; + [CEReactions] attribute DOMString title; + attribute DOMString dir; + attribute DOMString designMode; - [SetterRaisesException] attribute HTMLElement body; + readonly attribute USVString referrer; + [SetterMayThrowException] attribute USVString domain; + [ImplementedAs=urlForBindings] readonly attribute USVString URL; - readonly attribute HTMLHeadElement head; + [GetterMayThrowException, SetterMayThrowException] attribute USVString cookie; + + [CEReactions, DOMJIT=Getter, ImplementedAs=bodyOrFrameset, SetterMayThrowException] attribute HTMLElement? body; + + readonly attribute HTMLHeadElement? head; readonly attribute HTMLCollection images; readonly attribute HTMLCollection applets; readonly attribute HTMLCollection links; readonly attribute HTMLCollection forms; readonly attribute HTMLCollection anchors; + readonly attribute HTMLCollection embeds; + readonly attribute HTMLCollection plugins; + readonly attribute HTMLCollection scripts; readonly attribute DOMString lastModified; - NodeList getElementsByName([Default=Undefined] optional DOMString elementName); - -#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT - [Custom] attribute Location location; -#endif + NodeList getElementsByName([AtomicString] DOMString elementName); - // IE extensions + [PutForwards=href, Unforgeable] readonly attribute Location? location; - [TreatReturnedNullStringAs=Undefined, TreatNullAs=NullString] attribute DOMString charset; - [TreatReturnedNullStringAs=Undefined] readonly attribute DOMString defaultCharset; - [TreatReturnedNullStringAs=Undefined] readonly attribute DOMString readyState; + [ImplementedAs=characterSetWithUTF8Fallback] readonly attribute DOMString charset; + readonly attribute DOMString readyState; - Element elementFromPoint([Default=Undefined] optional long x, - [Default=Undefined] optional long y); - Range caretRangeFromPoint([Default=Undefined] optional long x, - [Default=Undefined] optional long y); + Range caretRangeFromPoint(optional long x = 0, optional long y = 0); // Mozilla extensions -#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT - DOMSelection getSelection(); -#endif - [TreatReturnedNullStringAs=Null] readonly attribute DOMString characterSet; - - // WebKit extensions + DOMSelection getSelection(); + [ImplementedAs=characterSetWithUTF8Fallback] readonly attribute DOMString characterSet; - [TreatReturnedNullStringAs=Null] readonly attribute DOMString preferredStylesheetSet; - [TreatReturnedNullStringAs=Null, TreatNullAs=NullString] attribute DOMString selectedStylesheetSet; - -#if !defined(LANGUAGE_JAVASCRIPT) || !LANGUAGE_JAVASCRIPT - CSSStyleDeclaration createCSSStyleDeclaration(); -#endif + readonly attribute DOMString? preferredStylesheetSet; + attribute DOMString? selectedStylesheetSet; -#if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C - // DOM Level 2 Style Interface - [ObjCLegacyUnnamedParameters, ObjCUseDefaultView] CSSStyleDeclaration getComputedStyle(Element element, - DOMString pseudoElement); - - // WebKit extension - // FIXME: remove the first version once optional is implemented for Objective-C. - [ObjCUseDefaultView] CSSRuleList getMatchedCSSRules(Element element, - DOMString pseudoElement); - [ObjCUseDefaultView] CSSRuleList getMatchedCSSRules(Element element, - DOMString pseudoElement, - optional boolean authorOnly); - -#endif + // FIXME: This is not standard and has been dropped from Blink already. + [Custom] (CanvasRenderingContext2D or WebGLRenderingContextBase) getCSSCanvasContext(DOMString contextId, DOMString name, long width, long height); -#if !defined(LANGUAGE_CPP) || !LANGUAGE_CPP -#if !defined(LANGUAGE_OBJECTIVE_C) || !LANGUAGE_OBJECTIVE_C - CanvasRenderingContext getCSSCanvasContext(DOMString contextId, DOMString name, long width, long height); -#endif -#endif + HTMLCollection getElementsByClassName(DOMString classNames); - // HTML 5 - NodeList getElementsByClassName([Default=Undefined] optional DOMString tagname); + boolean hasFocus(); readonly attribute DOMString compatMode; - // NodeSelector - Selector API - [RaisesException] Element querySelector(DOMString selectors); - [RaisesException] NodeList querySelectorAll(DOMString selectors); - #if defined(ENABLE_FULLSCREEN_API) && ENABLE_FULLSCREEN_API // Mozilla version readonly attribute boolean webkitIsFullScreen; readonly attribute boolean webkitFullScreenKeyboardInputAllowed; - readonly attribute Element webkitCurrentFullScreenElement; + [ImplementedAs=webkitCurrentFullScreenElementForBindings] readonly attribute Element webkitCurrentFullScreenElement; void webkitCancelFullScreen(); // W3C version readonly attribute boolean webkitFullscreenEnabled; - readonly attribute Element webkitFullscreenElement; + [ImplementedAs=webkitFullscreenElementForBindings] readonly attribute Element? webkitFullscreenElement; void webkitExitFullscreen(); #endif - [Conditional=POINTER_LOCK] void webkitExitPointerLock(); - [Conditional=POINTER_LOCK] readonly attribute Element webkitPointerLockElement; + [Conditional=POINTER_LOCK] void exitPointerLock(); [Conditional=CSS_REGIONS] DOMNamedFlowCollection webkitGetNamedFlows(); -#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT - [Conditional=FONT_LOAD_EVENTS] readonly attribute FontLoader fontloader; -#endif - -#if (!defined(LANGUAGE_OBJECTIVE_C) || !LANGUAGE_OBJECTIVE_C) && (!defined(LANGUAGE_GOBJECT) || !LANGUAGE_GOBJECT) - // Event handler DOM attributes - [NotEnumerable] attribute EventListener onabort; - [NotEnumerable] attribute EventListener onblur; - [NotEnumerable] attribute EventListener onchange; - [NotEnumerable] attribute EventListener onclick; - [NotEnumerable] attribute EventListener oncontextmenu; - [NotEnumerable] attribute EventListener ondblclick; - [NotEnumerable] attribute EventListener ondrag; - [NotEnumerable] attribute EventListener ondragend; - [NotEnumerable] attribute EventListener ondragenter; - [NotEnumerable] attribute EventListener ondragleave; - [NotEnumerable] attribute EventListener ondragover; - [NotEnumerable] attribute EventListener ondragstart; - [NotEnumerable] attribute EventListener ondrop; - [NotEnumerable] attribute EventListener onerror; - [NotEnumerable] attribute EventListener onfocus; - [NotEnumerable] attribute EventListener oninput; - [NotEnumerable] attribute EventListener oninvalid; - [NotEnumerable] attribute EventListener onkeydown; - [NotEnumerable] attribute EventListener onkeypress; - [NotEnumerable] attribute EventListener onkeyup; - [NotEnumerable] attribute EventListener onload; - [NotEnumerable] attribute EventListener onmousedown; - [NotEnumerable] attribute EventListener onmouseenter; - [NotEnumerable] attribute EventListener onmouseleave; - [NotEnumerable] attribute EventListener onmousemove; - [NotEnumerable] attribute EventListener onmouseout; - [NotEnumerable] attribute EventListener onmouseover; - [NotEnumerable] attribute EventListener onmouseup; - [NotEnumerable] attribute EventListener onmousewheel; - [NotEnumerable] attribute EventListener onreadystatechange; - [NotEnumerable] attribute EventListener onscroll; - [NotEnumerable] attribute EventListener onselect; - [NotEnumerable] attribute EventListener onsubmit; - [NotEnumerable] attribute EventListener onwheel; - - // [NotEnumerable] attribute EventListener oncanplay; - // [NotEnumerable] attribute EventListener oncanplaythrough; - // [NotEnumerable] attribute EventListener ondurationchange; - // [NotEnumerable] attribute EventListener onemptied; - // [NotEnumerable] attribute EventListener onended; - // [NotEnumerable] attribute EventListener onloadeddata; - // [NotEnumerable] attribute EventListener onloadedmetadata; - // [NotEnumerable] attribute EventListener onloadstart; - // [NotEnumerable] attribute EventListener onpause; - // [NotEnumerable] attribute EventListener onplay; - // [NotEnumerable] attribute EventListener onplaying; - // [NotEnumerable] attribute EventListener onprogress; - // [NotEnumerable] attribute EventListener onratechange; - // [NotEnumerable] attribute EventListener onseeked; - // [NotEnumerable] attribute EventListener onseeking; - // [NotEnumerable] attribute EventListener onshow; - // [NotEnumerable] attribute EventListener onstalled; - // [NotEnumerable] attribute EventListener onsuspend; - // [NotEnumerable] attribute EventListener ontimeupdate; - // [NotEnumerable] attribute EventListener onvolumechange; - // [NotEnumerable] attribute EventListener onwaiting; - - // WebKit extensions - [NotEnumerable] attribute EventListener onbeforecut; - [NotEnumerable] attribute EventListener oncut; - [NotEnumerable] attribute EventListener onbeforecopy; - [NotEnumerable] attribute EventListener oncopy; - [NotEnumerable] attribute EventListener onbeforepaste; - [NotEnumerable] attribute EventListener onpaste; - [NotEnumerable] attribute EventListener onreset; - [NotEnumerable] attribute EventListener onsearch; - [NotEnumerable] attribute EventListener onselectstart; - [NotEnumerable] attribute EventListener onselectionchange; - [NotEnumerable,Conditional=TOUCH_EVENTS] attribute EventListener ontouchstart; - [NotEnumerable,Conditional=TOUCH_EVENTS] attribute EventListener ontouchmove; - [NotEnumerable,Conditional=TOUCH_EVENTS] attribute EventListener ontouchend; - [NotEnumerable,Conditional=TOUCH_EVENTS] attribute EventListener ontouchcancel; - [NotEnumerable, Conditional=FULLSCREEN_API] attribute EventListener onwebkitfullscreenchange; - [NotEnumerable, Conditional=FULLSCREEN_API] attribute EventListener onwebkitfullscreenerror; - [NotEnumerable, Conditional=POINTER_LOCK] attribute EventListener onwebkitpointerlockchange; - [NotEnumerable, Conditional=POINTER_LOCK] attribute EventListener onwebkitpointerlockerror; - [NotEnumerable, Conditional=CSP_NEXT] attribute EventListener onsecuritypolicyviolation; -#endif + readonly attribute FontFaceSet fonts; -#if defined(ENABLE_TOUCH_EVENTS) && ENABLE_TOUCH_EVENTS - // FIXME: Consider defining an ENABLE macro for iOS touch event code, say IOS_TOUCH_EVENTS, - // and/or modifying the bindings scripts to support generating more complicated conditional code. -#if defined(WTF_PLATFORM_IOS) && WTF_PLATFORM_IOS +#if defined(ENABLE_IOS_TOUCH_EVENTS) && ENABLE_IOS_TOUCH_EVENTS #include <WebKitAdditions/DocumentIOS.idl> -#else - [ReturnNewObject, RaisesException] Touch createTouch([Default=Undefined] optional DOMWindow window, - [Default=Undefined] optional EventTarget target, - [Default=Undefined] optional long identifier, - [Default=Undefined] optional long pageX, - [Default=Undefined] optional long pageY, - [Default=Undefined] optional long screenX, - [Default=Undefined] optional long screenY, - [Default=Undefined] optional long webkitRadiusX, - [Default=Undefined] optional long webkitRadiusY, - [Default=Undefined] optional float webkitRotationAngle, - [Default=Undefined] optional float webkitForce); - [ReturnNewObject, Custom, RaisesException] TouchList createTouchList(); -#endif // defined(WTF_PLATFORM_IOS) && WTF_PLATFORM_IOS -#endif - -#if defined(LANGUAGE_CPP) && LANGUAGE_CPP - // Extra WebCore methods exposed to allow compile-time casting in C++ - boolean isHTMLDocument(); +#elif defined(ENABLE_TOUCH_EVENTS) && ENABLE_TOUCH_EVENTS + [NewObject] Touch createTouch(optional DOMWindow? window = null, optional EventTarget? target = null, + optional long identifier = 0, + optional long pageX = 0, optional long pageY = 0, optional long screenX = 0, optional long screenY = 0, + optional long webkitRadiusX = 0, optional long webkitRadiusY = 0, + optional unrestricted float webkitRotationAngle = NaN, optional unrestricted float webkitForce = NaN); + [NewObject, Custom] TouchList createTouchList(Touch... touches); #endif // Page visibility API. - [Conditional=PAGE_VISIBILITY_API] readonly attribute DOMString visibilityState; - [Conditional=PAGE_VISIBILITY_API] readonly attribute boolean hidden; - - // Security Policy API: http://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html#script-interfaces - [Conditional=CSP_NEXT] readonly attribute DOMSecurityPolicy securityPolicy; + readonly attribute VisibilityState visibilityState; + readonly attribute boolean hidden; + attribute EventHandler onvisibilitychange; // currentscript API: http://www.whatwg.org/specs/web-apps/current-work/multipage/dom.html#dom-document-currentscript - readonly attribute HTMLScriptElement currentScript; + readonly attribute HTMLScriptElement? currentScript; + + // http://www.w3.org/TR/2014/WD-dom-20140204/#dom-document-origin + readonly attribute USVString origin; + + // http://dev.w3.org/csswg/cssom-view/#dom-document-scrollingelement + readonly attribute Element? scrollingElement; + + // Unique to Element and Document + // FIXME: Should these be exposed on Window as well (and therefore moved to GlobalEventHandlers.idl)? + [NotEnumerable] attribute EventHandler onbeforecopy; + [NotEnumerable] attribute EventHandler onbeforecut; + [NotEnumerable] attribute EventHandler onbeforeinput; + [NotEnumerable] attribute EventHandler onbeforepaste; + [NotEnumerable] attribute EventHandler oncopy; + [NotEnumerable] attribute EventHandler oncut; + [NotEnumerable] attribute EventHandler onpaste; + [NotEnumerable] attribute EventHandler onselectstart; + [NotEnumerable, Conditional=FULLSCREEN_API] attribute EventHandler onwebkitfullscreenchange; + [NotEnumerable, Conditional=FULLSCREEN_API] attribute EventHandler onwebkitfullscreenerror; + + // Unique to Document and HTMLBodyElement + [NotEnumerable] attribute EventHandler onselectionchange; + + // Unique to Document + [LenientThis] attribute EventHandler onreadystatechange; + [NotEnumerable, Conditional=POINTER_LOCK] attribute EventHandler onpointerlockchange; + [NotEnumerable, Conditional=POINTER_LOCK] attribute EventHandler onpointerlockerror; +}; + +enum VisibilityState { + "hidden", + "visible", + "prerender" }; +Document implements ParentNode; +Document implements NonElementParentNode; +Document implements DocumentOrShadowRoot; +Document implements GlobalEventHandlers; diff --git a/Source/WebCore/dom/DocumentEventQueue.cpp b/Source/WebCore/dom/DocumentEventQueue.cpp index 631653ab7..9e077f72f 100644 --- a/Source/WebCore/dom/DocumentEventQueue.cpp +++ b/Source/WebCore/dom/DocumentEventQueue.cpp @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -31,6 +31,7 @@ #include "DOMWindow.h" #include "Document.h" #include "Event.h" +#include "EventNames.h" #include "SuspendableTimer.h" #include <wtf/Ref.h> @@ -38,30 +39,27 @@ namespace WebCore { class DocumentEventQueue::Timer final : public SuspendableTimer { public: - static PassOwnPtr<Timer> create(DocumentEventQueue& eventQueue) - { - return adoptPtr(new Timer(eventQueue)); - } - -private: Timer(DocumentEventQueue& eventQueue) - : SuspendableTimer(&eventQueue.m_document) + : SuspendableTimer(eventQueue.m_document) , m_eventQueue(eventQueue) { } - virtual void fired() override +private: + void fired() override { ASSERT(!isSuspended()); m_eventQueue.pendingEventTimerFired(); } + const char* activeDOMObjectName() const override { return "DocumentEventQueueTimer"; } + DocumentEventQueue& m_eventQueue; }; DocumentEventQueue::DocumentEventQueue(Document& document) : m_document(document) - , m_pendingEventTimer(Timer::create(*this)) + , m_pendingEventTimer(std::make_unique<Timer>(*this)) , m_isClosed(false) { m_pendingEventTimer->suspendIfNeeded(); @@ -71,15 +69,15 @@ DocumentEventQueue::~DocumentEventQueue() { } -bool DocumentEventQueue::enqueueEvent(PassRefPtr<Event> event) +bool DocumentEventQueue::enqueueEvent(Ref<Event>&& event) { ASSERT(event->target()); - ASSERT(!m_queuedEvents.contains(event.get())); + ASSERT(!m_queuedEvents.contains(event.ptr())); if (m_isClosed) return false; - m_queuedEvents.add(event); + m_queuedEvents.add(event.ptr()); if (!m_pendingEventTimer->isActive()) m_pendingEventTimer->startOneShot(0); return true; @@ -102,9 +100,9 @@ void DocumentEventQueue::enqueueOrDispatchScrollEvent(Node& target) bool bubbles = target.isDocumentNode(); bool cancelable = false; - RefPtr<Event> scrollEvent = Event::create(eventNames().scrollEvent, bubbles, cancelable); + Ref<Event> scrollEvent = Event::create(eventNames().scrollEvent, bubbles, cancelable); scrollEvent->setTarget(&target); - enqueueEvent(scrollEvent.release()); + enqueueEvent(WTFMove(scrollEvent)); } bool DocumentEventQueue::cancelEvent(Event& event) @@ -149,9 +147,9 @@ void DocumentEventQueue::dispatchEvent(Event& event) // Why do we have this special case here instead of a virtual function on EventTarget? EventTarget& eventTarget = *event.target(); if (DOMWindow* window = eventTarget.toDOMWindow()) - window->dispatchEvent(&event, 0); + window->dispatchEvent(event, nullptr); else - eventTarget.dispatchEvent(&event); + eventTarget.dispatchEvent(event); } } diff --git a/Source/WebCore/dom/DocumentEventQueue.h b/Source/WebCore/dom/DocumentEventQueue.h index 3ac05e4f4..973b0d136 100644 --- a/Source/WebCore/dom/DocumentEventQueue.h +++ b/Source/WebCore/dom/DocumentEventQueue.h @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -25,13 +25,12 @@ * */ -#ifndef DocumentEventQueue_h -#define DocumentEventQueue_h +#pragma once #include "EventQueue.h" +#include <memory> #include <wtf/HashSet.h> #include <wtf/ListHashSet.h> -#include <wtf/OwnPtr.h> namespace WebCore { @@ -44,9 +43,9 @@ public: explicit DocumentEventQueue(Document&); virtual ~DocumentEventQueue(); - virtual bool enqueueEvent(PassRefPtr<Event>) override; - virtual bool cancelEvent(Event&) override; - virtual void close() override; + bool enqueueEvent(Ref<Event>&&) override; + bool cancelEvent(Event&) override; + void close() override; void enqueueOrDispatchScrollEvent(Node&); @@ -57,12 +56,10 @@ private: class Timer; Document& m_document; - OwnPtr<Timer> m_pendingEventTimer; - ListHashSet<RefPtr<Event>, 16> m_queuedEvents; + std::unique_ptr<Timer> m_pendingEventTimer; + ListHashSet<RefPtr<Event>> m_queuedEvents; HashSet<Node*> m_nodesWithQueuedScrollEvents; bool m_isClosed; }; -} - -#endif // DocumentEventQueue_h +} // namespace WebCore diff --git a/Source/WebCore/dom/DocumentFragment.cpp b/Source/WebCore/dom/DocumentFragment.cpp index b11af5ddb..5c9351d3e 100644 --- a/Source/WebCore/dom/DocumentFragment.cpp +++ b/Source/WebCore/dom/DocumentFragment.cpp @@ -24,31 +24,26 @@ #include "DocumentFragment.h" #include "Document.h" +#include "ElementDescendantIterator.h" #include "HTMLDocumentParser.h" #include "Page.h" -#include "Settings.h" #include "XMLDocumentParser.h" namespace WebCore { -DocumentFragment::DocumentFragment(Document* document, ConstructionType constructionType) +DocumentFragment::DocumentFragment(Document& document, ConstructionType constructionType) : ContainerNode(document, constructionType) { } -PassRefPtr<DocumentFragment> DocumentFragment::create(Document& document) +Ref<DocumentFragment> DocumentFragment::create(Document& document) { - return adoptRef(new DocumentFragment(&document, Node::CreateDocumentFragment)); -} - -PassRefPtr<DocumentFragment> DocumentFragment::create(ScriptExecutionContext& context) -{ - return adoptRef(new DocumentFragment(&toDocument(context), Node::CreateDocumentFragment)); + return adoptRef(*new DocumentFragment(document, Node::CreateDocumentFragment)); } String DocumentFragment::nodeName() const { - return "#document-fragment"; + return ASCIILiteral("#document-fragment"); } Node::NodeType DocumentFragment::nodeType() const @@ -64,24 +59,30 @@ bool DocumentFragment::childTypeAllowed(NodeType type) const case COMMENT_NODE: case TEXT_NODE: case CDATA_SECTION_NODE: - case ENTITY_REFERENCE_NODE: return true; default: return false; } } -PassRefPtr<Node> DocumentFragment::cloneNode(bool deep) +Ref<Node> DocumentFragment::cloneNodeInternal(Document& targetDocument, CloningOperation type) { - RefPtr<DocumentFragment> clone = create(document()); - if (deep) - cloneChildNodes(clone.get()); - return clone.release(); + Ref<DocumentFragment> clone = create(targetDocument); + switch (type) { + case CloningOperation::OnlySelf: + case CloningOperation::SelfWithTemplateContent: + break; + case CloningOperation::Everything: + cloneChildNodes(clone); + break; + } + return WTFMove(clone); } void DocumentFragment::parseHTML(const String& source, Element* contextElement, ParserContentPolicy parserContentPolicy) { - HTMLDocumentParser::parseDocumentFragment(source, *this, contextElement, parserContentPolicy); + ASSERT(contextElement); + HTMLDocumentParser::parseDocumentFragment(source, *this, *contextElement, parserContentPolicy); } bool DocumentFragment::parseXML(const String& source, Element* contextElement, ParserContentPolicy parserContentPolicy) @@ -89,4 +90,22 @@ bool DocumentFragment::parseXML(const String& source, Element* contextElement, P return XMLDocumentParser::parseDocumentFragment(source, *this, contextElement, parserContentPolicy); } +Element* DocumentFragment::getElementById(const AtomicString& id) const +{ + if (id.isNull()) + return nullptr; + + // Fast path for ShadowRoot, where we are both a DocumentFragment and a TreeScope. + if (isTreeScope()) + return treeScope().getElementById(id); + + // Otherwise, fall back to iterating all of the element descendants. + for (auto& element : elementDescendants(*this)) { + if (element.getIdAttribute() == id) + return const_cast<Element*>(&element); + } + + return nullptr; +} + } diff --git a/Source/WebCore/dom/DocumentFragment.h b/Source/WebCore/dom/DocumentFragment.h index 9f1b1afa9..9d00c6c50 100644 --- a/Source/WebCore/dom/DocumentFragment.h +++ b/Source/WebCore/dom/DocumentFragment.h @@ -21,42 +21,38 @@ * */ -#ifndef DocumentFragment_h -#define DocumentFragment_h +#pragma once #include "ContainerNode.h" #include "FragmentScriptingPermission.h" namespace WebCore { -class ScriptExecutionContext; - class DocumentFragment : public ContainerNode { public: - static PassRefPtr<DocumentFragment> create(Document&); - static PassRefPtr<DocumentFragment> create(ScriptExecutionContext&); + static Ref<DocumentFragment> create(Document&); void parseHTML(const String&, Element* contextElement, ParserContentPolicy = AllowScriptingContent); bool parseXML(const String&, Element* contextElement, ParserContentPolicy = AllowScriptingContent); - virtual bool canContainRangeEndPoint() const override { return true; } + bool canContainRangeEndPoint() const final { return true; } virtual bool isTemplateContent() const { return false; } + // From the NonElementParentNode interface - https://dom.spec.whatwg.org/#interface-nonelementparentnode + WEBCORE_EXPORT Element* getElementById(const AtomicString&) const; + protected: - DocumentFragment(Document*, ConstructionType = CreateContainer); - virtual String nodeName() const override; + DocumentFragment(Document&, ConstructionType = CreateContainer); + String nodeName() const final; private: - virtual NodeType nodeType() const override; - virtual PassRefPtr<Node> cloneNode(bool deep) override; - virtual bool childTypeAllowed(NodeType) const override; + NodeType nodeType() const final; + Ref<Node> cloneNodeInternal(Document&, CloningOperation) override; + bool childTypeAllowed(NodeType) const override; }; -inline bool isDocumentFragment(const Node& node) { return node.isDocumentFragment(); } -void isDocumentFragment(const DocumentFragment&); // Catch unnecessary runtime check of type known at compile time. - -NODE_TYPE_CASTS(DocumentFragment) - -} //namespace +} // namespace WebCore -#endif +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::DocumentFragment) + static bool isType(const WebCore::Node& node) { return node.isDocumentFragment(); } +SPECIALIZE_TYPE_TRAITS_END() diff --git a/Source/WebCore/dom/DocumentFragment.idl b/Source/WebCore/dom/DocumentFragment.idl index 6c47ad911..c6748a853 100644 --- a/Source/WebCore/dom/DocumentFragment.idl +++ b/Source/WebCore/dom/DocumentFragment.idl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008, 2015 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -17,12 +17,12 @@ * Boston, MA 02110-1301, USA. */ - [ +[ Constructor, - ConstructorCallWith=ScriptExecutionContext - ] interface DocumentFragment : Node { - // NodeSelector - Selector API - [RaisesException] Element querySelector(DOMString selectors); - [RaisesException] NodeList querySelectorAll(DOMString selectors); + ConstructorCallWith=Document, + CustomToJSObject +] interface DocumentFragment : Node { }; +DocumentFragment implements ParentNode; +DocumentFragment implements NonElementParentNode; diff --git a/Source/WebCore/dom/DocumentMarker.cpp b/Source/WebCore/dom/DocumentMarker.cpp deleted file mode 100644 index a1bb5255b..000000000 --- a/Source/WebCore/dom/DocumentMarker.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "DocumentMarker.h" - -namespace WebCore { - -DocumentMarkerDetails::~DocumentMarkerDetails() -{ -} - -class DocumentMarkerDescription : public DocumentMarkerDetails { -public: - static PassRefPtr<DocumentMarkerDescription> create(const String&); - - const String& description() const { return m_description; } - virtual bool isDescription() const override { return true; } - -private: - DocumentMarkerDescription(const String& description) - : m_description(description) - { - } - - String m_description; -}; - -PassRefPtr<DocumentMarkerDescription> DocumentMarkerDescription::create(const String& description) -{ - return adoptRef(new DocumentMarkerDescription(description)); -} - -inline DocumentMarkerDescription* toDocumentMarkerDescription(DocumentMarkerDetails* details) -{ - if (details && details->isDescription()) - return static_cast<DocumentMarkerDescription*>(details); - return 0; -} - - -class DocumentMarkerTextMatch : public DocumentMarkerDetails { -public: - static PassRefPtr<DocumentMarkerTextMatch> instanceFor(bool); - - bool activeMatch() const { return m_match; } - virtual bool isTextMatch() const override { return true; } - -private: - explicit DocumentMarkerTextMatch(bool match) - : m_match(match) - { - } - - bool m_match; -}; - -PassRefPtr<DocumentMarkerTextMatch> DocumentMarkerTextMatch::instanceFor(bool match) -{ - static DocumentMarkerTextMatch* trueInstance = adoptRef(new DocumentMarkerTextMatch(true)).leakRef(); - static DocumentMarkerTextMatch* falseInstance = adoptRef(new DocumentMarkerTextMatch(false)).leakRef(); - return match ? trueInstance : falseInstance; -} - -inline DocumentMarkerTextMatch* toDocumentMarkerTextMatch(DocumentMarkerDetails* details) -{ - if (details && details->isTextMatch()) - return static_cast<DocumentMarkerTextMatch*>(details); - return 0; -} - - -DocumentMarker::DocumentMarker() - : m_type(Spelling), m_startOffset(0), m_endOffset(0) -{ -} - -DocumentMarker::DocumentMarker(MarkerType type, unsigned startOffset, unsigned endOffset) - : m_type(type), m_startOffset(startOffset), m_endOffset(endOffset) -{ -} - -DocumentMarker::DocumentMarker(MarkerType type, unsigned startOffset, unsigned endOffset, const String& description) - : m_type(type) - , m_startOffset(startOffset) - , m_endOffset(endOffset) - , m_details(description.isEmpty() ? 0 : DocumentMarkerDescription::create(description)) -{ -} - -DocumentMarker::DocumentMarker(unsigned startOffset, unsigned endOffset, bool activeMatch) - : m_type(DocumentMarker::TextMatch) - , m_startOffset(startOffset) - , m_endOffset(endOffset) - , m_details(DocumentMarkerTextMatch::instanceFor(activeMatch)) -{ -} - -DocumentMarker::DocumentMarker(MarkerType type, unsigned startOffset, unsigned endOffset, PassRefPtr<DocumentMarkerDetails> details) - : m_type(type) - , m_startOffset(startOffset) - , m_endOffset(endOffset) - , m_details(details) -{ -} - -void DocumentMarker::shiftOffsets(int delta) -{ - m_startOffset += delta; - m_endOffset += delta; -} - -void DocumentMarker::setActiveMatch(bool active) -{ - m_details = DocumentMarkerTextMatch::instanceFor(active); -} - -const String& DocumentMarker::description() const -{ - if (DocumentMarkerDescription* details = toDocumentMarkerDescription(m_details.get())) - return details->description(); - return emptyString(); -} - -bool DocumentMarker::activeMatch() const -{ - if (DocumentMarkerTextMatch* details = toDocumentMarkerTextMatch(m_details.get())) - return details->activeMatch(); - return false; -} - -} // namespace WebCore diff --git a/Source/WebCore/dom/DocumentMarker.h b/Source/WebCore/dom/DocumentMarker.h index b3ac55900..904bf8c3f 100644 --- a/Source/WebCore/dom/DocumentMarker.h +++ b/Source/WebCore/dom/DocumentMarker.h @@ -1,7 +1,5 @@ /* - * This file is part of the DOM implementation for WebCore. - * - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006-2016 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -20,12 +18,10 @@ * */ -#ifndef DocumentMarker_h -#define DocumentMarker_h +#pragma once #include <wtf/Forward.h> -#include <wtf/RefCounted.h> -#include <wtf/RefPtr.h> +#include <wtf/Variant.h> #include <wtf/text/WTFString.h> #if PLATFORM(IOS) @@ -35,8 +31,6 @@ typedef struct objc_object *id; namespace WebCore { -class DocumentMarkerDetails; - // A range of a node within a document that is "marked", such as the range of a misspelled word. // It optionally includes a description that could be displayed in the user interface. // It also optionally includes a flag specifying whether the match is active, which is ignored @@ -71,24 +65,29 @@ public: // This marker indicates that the range of text spanned by the marker is entered by voice dictation, // and it has alternative text. DictationAlternatives = 1 << 9, +#if ENABLE(TELEPHONE_NUMBER_DETECTION) + TelephoneNumber = 1 << 10, +#endif #if PLATFORM(IOS) - // FIXME: iOS has its own dictation marks. iOS should use OpenSource's. - DictationPhraseWithAlternatives = 1 << 10, - DictationResult = 1 << 11, + // FIXME: iOS should share the same dictation mark system with the other platforms <rdar://problem/9431249>. + DictationPhraseWithAlternatives = 1 << 11, + DictationResult = 1 << 12, #endif + // This marker indicates that the user has selected a text candidate. + AcceptedCandidate = 1 << 13, }; class MarkerTypes { public: - // The constructor is intentionally implicit to allow conversion from the bit-wise sum of above types + // This constructor is left implicit to allow conversion from result of a bit-wise or of enumeration values. MarkerTypes(unsigned mask) : m_mask(mask) { } bool contains(MarkerType type) const { return m_mask & type; } - bool intersects(const MarkerTypes& types) const { return (m_mask & types.m_mask); } - bool operator==(const MarkerTypes& other) const { return m_mask == other.m_mask; } + bool intersects(MarkerTypes types) const { return m_mask & types.m_mask; } + bool operator==(MarkerTypes other) const { return m_mask == other.m_mask; } - void add(const MarkerTypes& types) { m_mask |= types.m_mask; } - void remove(const MarkerTypes& types) { m_mask &= ~types.m_mask; } + void add(MarkerTypes types) { m_mask |= types.m_mask; } + void remove(MarkerTypes types) { m_mask &= ~types.m_mask; } private: unsigned m_mask; @@ -97,34 +96,62 @@ public: class AllMarkers : public MarkerTypes { public: AllMarkers() -#if !PLATFORM(IOS) - : MarkerTypes(Spelling | Grammar | TextMatch | Replacement | CorrectionIndicator | RejectedCorrection | Autocorrected | SpellCheckingExemption | DeletedAutocorrection | DictationAlternatives) -#else - : MarkerTypes(Spelling | Grammar | TextMatch | Replacement | CorrectionIndicator | RejectedCorrection | Autocorrected | SpellCheckingExemption | DeletedAutocorrection | DictationAlternatives | DictationPhraseWithAlternatives | DictationResult) -#endif // !PLATFORM(IOS) + : MarkerTypes(0 + | AcceptedCandidate + | Autocorrected + | CorrectionIndicator + | DeletedAutocorrection + | DictationAlternatives + | Grammar + | RejectedCorrection + | Replacement + | SpellCheckingExemption + | Spelling + | TextMatch +#if ENABLE(TELEPHONE_NUMBER_DETECTION) + | TelephoneNumber +#endif +#if PLATFORM(IOS) + | DictationPhraseWithAlternatives + | DictationResult +#endif + ) { } }; - DocumentMarker(); - DocumentMarker(MarkerType, unsigned startOffset, unsigned endOffset); - DocumentMarker(MarkerType, unsigned startOffset, unsigned endOffset, const String& description); + using IsActiveMatchData = bool; + using DescriptionData = String; + struct DictationData { + uint64_t context; + String originalText; + }; + struct DictationAlternativesData { +#if PLATFORM(IOS) + Vector<String> alternatives; + RetainPtr<id> metadata; +#endif + }; + using Data = Variant<IsActiveMatchData, DescriptionData, DictationData, DictationAlternativesData>; + + DocumentMarker(unsigned startOffset, unsigned endOffset, bool isActiveMatch); + DocumentMarker(MarkerType, unsigned startOffset, unsigned endOffset, const String& description = String()); + DocumentMarker(MarkerType, unsigned startOffset, unsigned endOffset, Data&&); #if PLATFORM(IOS) DocumentMarker(MarkerType, unsigned startOffset, unsigned endOffset, const String& description, const Vector<String>& alternatives, RetainPtr<id> metadata); #endif - DocumentMarker(unsigned startOffset, unsigned endOffset, bool activeMatch); - DocumentMarker(MarkerType, unsigned startOffset, unsigned endOffset, PassRefPtr<DocumentMarkerDetails>); MarkerType type() const { return m_type; } unsigned startOffset() const { return m_startOffset; } unsigned endOffset() const { return m_endOffset; } const String& description() const; - bool activeMatch() const; - DocumentMarkerDetails* details() const; + bool isActiveMatch() const; void setActiveMatch(bool); - void clearDetails() { m_details.clear(); } + + const Data& data() const { return m_data; } + void clearData() { m_data = false; } // Offset modifications are done by DocumentMarkerController. // Other classes should not call following setters. @@ -139,79 +166,92 @@ public: void setMetadata(id); #endif - bool operator==(const DocumentMarker& o) const - { - return type() == o.type() && startOffset() == o.startOffset() && endOffset() == o.endOffset(); - } - - bool operator!=(const DocumentMarker& o) const - { - return !(*this == o); - } - private: MarkerType m_type; unsigned m_startOffset; unsigned m_endOffset; -#if PLATFORM(IOS) - // FIXME: See <rdar://problem/9431249>. - Vector<String> m_alternatives; - RetainPtr<id> m_metadata; -#endif - RefPtr<DocumentMarkerDetails> m_details; + Data m_data; }; -inline DocumentMarkerDetails* DocumentMarker::details() const +inline DocumentMarker::DocumentMarker(unsigned startOffset, unsigned endOffset, bool isActiveMatch) + : m_type(TextMatch) + , m_startOffset(startOffset) + , m_endOffset(endOffset) + , m_data(isActiveMatch) +{ +} + +inline DocumentMarker::DocumentMarker(MarkerType type, unsigned startOffset, unsigned endOffset, const String& description) + : m_type(type) + , m_startOffset(startOffset) + , m_endOffset(endOffset) + , m_data(description) +{ +} + +inline DocumentMarker::DocumentMarker(MarkerType type, unsigned startOffset, unsigned endOffset, Data&& data) + : m_type(type) + , m_startOffset(startOffset) + , m_endOffset(endOffset) + , m_data(WTFMove(data)) +{ +} + +inline void DocumentMarker::shiftOffsets(int delta) +{ + m_startOffset += delta; + m_endOffset += delta; +} + +inline const String& DocumentMarker::description() const +{ + return WTF::holds_alternative<String>(m_data) ? WTF::get<String>(m_data) : emptyString(); +} + +inline bool DocumentMarker::isActiveMatch() const { - return m_details.get(); + return WTF::holds_alternative<bool>(m_data) && WTF::get<bool>(m_data); +} + +inline void DocumentMarker::setActiveMatch(bool isActiveMatch) +{ + ASSERT(m_type == TextMatch); + m_data = isActiveMatch; } #if PLATFORM(IOS) + +// FIXME: iOS should share the same dictation mark system with the other platforms <rdar://problem/9431249>. + inline DocumentMarker::DocumentMarker(MarkerType type, unsigned startOffset, unsigned endOffset, const String&, const Vector<String>& alternatives, RetainPtr<id> metadata) : m_type(type) , m_startOffset(startOffset) , m_endOffset(endOffset) - , m_alternatives(alternatives) - , m_metadata(metadata) + , m_data(DictationAlternativesData { alternatives, metadata }) { - // FIXME: <rdar://problem/11306422> iOS should investigate cleaner merge with ToT Dictation support ASSERT(type == DictationPhraseWithAlternatives || type == DictationResult); } -#endif -#if PLATFORM(IOS) inline const Vector<String>& DocumentMarker::alternatives() const { - ASSERT(m_type == DocumentMarker::DictationPhraseWithAlternatives); - return m_alternatives; + return WTF::get<DictationAlternativesData>(m_data).alternatives; } inline void DocumentMarker::setAlternative(const String& alternative, size_t index) { - ASSERT(m_type == DocumentMarker::DictationPhraseWithAlternatives); - m_alternatives[index] = alternative; + WTF::get<DictationAlternativesData>(m_data).alternatives[index] = alternative; } inline id DocumentMarker::metadata() const { - return m_metadata.get(); + return WTF::get<DictationAlternativesData>(m_data).metadata.get(); } inline void DocumentMarker::setMetadata(id metadata) { - m_metadata = metadata; + WTF::get<DictationAlternativesData>(m_data).metadata = metadata; } -#endif -class DocumentMarkerDetails : public RefCounted<DocumentMarkerDetails> -{ -public: - DocumentMarkerDetails() { } - virtual ~DocumentMarkerDetails(); - virtual bool isDescription() const { return false; } - virtual bool isTextMatch() const { return false; } -}; +#endif } // namespace WebCore - -#endif // DocumentMarker_h diff --git a/Source/WebCore/dom/DocumentMarkerController.cpp b/Source/WebCore/dom/DocumentMarkerController.cpp index bcc7369cc..ccddcf58c 100644 --- a/Source/WebCore/dom/DocumentMarkerController.cpp +++ b/Source/WebCore/dom/DocumentMarkerController.cpp @@ -27,9 +27,15 @@ #include "config.h" #include "DocumentMarkerController.h" +#include "Chrome.h" +#include "ChromeClient.h" +#include "MainFrame.h" #include "NodeTraversal.h" +#include "Page.h" #include "Range.h" -#include "RenderObject.h" +#include "RenderBlockFlow.h" +#include "RenderLayer.h" +#include "RenderText.h" #include "RenderedDocumentMarker.h" #include "TextIterator.h" #include <stdio.h> @@ -41,8 +47,8 @@ inline bool DocumentMarkerController::possiblyHasMarkers(DocumentMarker::MarkerT return m_possiblyExistingMarkerTypes.intersects(types); } -DocumentMarkerController::DocumentMarkerController() - : m_possiblyExistingMarkerTypes(0) +DocumentMarkerController::DocumentMarkerController(Document& document) + : m_document(document) { } @@ -58,19 +64,17 @@ void DocumentMarkerController::detach() void DocumentMarkerController::addMarker(Range* range, DocumentMarker::MarkerType type, const String& description) { - // Use a TextIterator to visit the potentially multiple nodes the range covers. for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) { RefPtr<Range> textPiece = markedText.range(); - addMarker(textPiece->startContainer(), DocumentMarker(type, textPiece->startOffset(), textPiece->endOffset(), description)); + addMarker(&textPiece->startContainer(), DocumentMarker(type, textPiece->startOffset(), textPiece->endOffset(), description)); } } void DocumentMarkerController::addMarker(Range* range, DocumentMarker::MarkerType type) { - // Use a TextIterator to visit the potentially multiple nodes the range covers. for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) { RefPtr<Range> textPiece = markedText.range(); - addMarker(textPiece->startContainer(), DocumentMarker(type, textPiece->startOffset(), textPiece->endOffset())); + addMarker(&textPiece->startContainer(), DocumentMarker(type, textPiece->startOffset(), textPiece->endOffset())); } } @@ -80,39 +84,28 @@ void DocumentMarkerController::addMarkerToNode(Node* node, unsigned startOffset, addMarker(node, DocumentMarker(type, startOffset, startOffset + length)); } -void DocumentMarkerController::addMarkerToNode(Node* node, unsigned startOffset, unsigned length, DocumentMarker::MarkerType type, PassRefPtr<DocumentMarkerDetails> details) +void DocumentMarkerController::addMarkerToNode(Node* node, unsigned startOffset, unsigned length, DocumentMarker::MarkerType type, DocumentMarker::Data&& data) { - addMarker(node, DocumentMarker(type, startOffset, startOffset + length, details)); + addMarker(node, DocumentMarker(type, startOffset, startOffset + length, WTFMove(data))); } - void DocumentMarkerController::addTextMatchMarker(const Range* range, bool activeMatch) { - // Use a TextIterator to visit the potentially multiple nodes the range covers. for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) { RefPtr<Range> textPiece = markedText.range(); unsigned startOffset = textPiece->startOffset(); unsigned endOffset = textPiece->endOffset(); - addMarker(textPiece->startContainer(), DocumentMarker(startOffset, endOffset, activeMatch)); - if (endOffset > startOffset) { - // Rendered rects for markers in WebKit are not populated until each time - // the markers are painted. However, we need it to happen sooner, because - // the whole purpose of tickmarks on the scrollbar is to show where - // matches off-screen are (that haven't been painted yet). - Node* node = textPiece->startContainer(); - Vector<DocumentMarker*> markers = markersFor(node); - static_cast<RenderedDocumentMarker*>(markers[markers.size() - 1])->setRenderedRect(range->boundingBox()); - } + addMarker(&textPiece->startContainer(), DocumentMarker(startOffset, endOffset, activeMatch)); } } #if PLATFORM(IOS) -void DocumentMarkerController::addMarker(Range* range, DocumentMarker::MarkerType type, String description, const Vector<String>& interpretations, const RetainPtr<id>& metadata) + +void DocumentMarkerController::addMarker(Range* range, DocumentMarker::MarkerType type, const String& description, const Vector<String>& interpretations, const RetainPtr<id>& metadata) { - // Use a TextIterator to visit the potentially multiple nodes the range covers. for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) { RefPtr<Range> textPiece = markedText.range(); - addMarker(textPiece->startContainer(), DocumentMarker(type, textPiece->startOffset(), textPiece->endOffset(), description, interpretations, metadata)); + addMarker(&textPiece->startContainer(), DocumentMarker(type, textPiece->startOffset(), textPiece->endOffset(), description, interpretations, metadata)); } } @@ -123,24 +116,23 @@ void DocumentMarkerController::addDictationPhraseWithAlternativesMarker(Range* r return; size_t numberOfAlternatives = interpretations.size() - 1; - // Use a TextIterator to visit the potentially multiple nodes the range covers. for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) { RefPtr<Range> textPiece = markedText.range(); - DocumentMarker marker(DocumentMarker::DictationPhraseWithAlternatives, textPiece->startOffset(), textPiece->endOffset(), "", Vector<String>(numberOfAlternatives), RetainPtr<id>()); + DocumentMarker marker(DocumentMarker::DictationPhraseWithAlternatives, textPiece->startOffset(), textPiece->endOffset(), emptyString(), Vector<String>(numberOfAlternatives), RetainPtr<id>()); for (size_t i = 0; i < numberOfAlternatives; ++i) marker.setAlternative(interpretations[i + 1], i); - addMarker(textPiece->startContainer(), marker); + addMarker(&textPiece->startContainer(), marker); } } void DocumentMarkerController::addDictationResultMarker(Range* range, const RetainPtr<id>& metadata) { - // Use a TextIterator to visit the potentially multiple nodes the range covers. for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) { RefPtr<Range> textPiece = markedText.range(); - addMarker(textPiece->startContainer(), DocumentMarker(DocumentMarker::DictationResult, textPiece->startOffset(), textPiece->endOffset(), String(), Vector<String>(), metadata)); + addMarker(&textPiece->startContainer(), DocumentMarker(DocumentMarker::DictationResult, textPiece->startOffset(), textPiece->endOffset(), String(), Vector<String>(), metadata)); } } + #endif void DocumentMarkerController::removeMarkers(Range* range, DocumentMarker::MarkerTypes markerTypes, RemovePartiallyOverlappingMarkerOrNot shouldRemovePartiallyOverlappingMarker) @@ -151,10 +143,154 @@ void DocumentMarkerController::removeMarkers(Range* range, DocumentMarker::Marke ASSERT(!m_markers.isEmpty()); RefPtr<Range> textPiece = markedText.range(); - int startOffset = textPiece->startOffset(); - int endOffset = textPiece->endOffset(); - removeMarkers(textPiece->startContainer(), startOffset, endOffset - startOffset, markerTypes, shouldRemovePartiallyOverlappingMarker); + unsigned startOffset = textPiece->startOffset(); + unsigned endOffset = textPiece->endOffset(); + removeMarkers(&textPiece->startContainer(), startOffset, endOffset - startOffset, markerTypes, shouldRemovePartiallyOverlappingMarker); + } +} + +static void updateRenderedRectsForMarker(RenderedDocumentMarker& marker, Node& node) +{ + ASSERT(!node.document().view() || !node.document().view()->needsLayout()); + + // FIXME: We should refactor this so that we don't use Range (because we only have one Node), but still share code with absoluteTextQuads(). + RefPtr<Range> markerRange = Range::create(node.document(), &node, marker.startOffset(), &node, marker.endOffset()); + if (!markerRange) + return; + Vector<FloatQuad> absoluteMarkerQuads; + markerRange->absoluteTextQuads(absoluteMarkerQuads, true); + + Vector<FloatRect> absoluteMarkerRects; + absoluteMarkerRects.reserveInitialCapacity(absoluteMarkerQuads.size()); + for (const auto& quad : absoluteMarkerQuads) + absoluteMarkerRects.uncheckedAppend(quad.boundingBox()); + + marker.setUnclippedAbsoluteRects(absoluteMarkerRects); +} + +void DocumentMarkerController::invalidateRectsForAllMarkers() +{ + if (!hasMarkers()) + return; + + for (auto& markers : m_markers.values()) { + for (auto& marker : *markers) + marker.invalidate(); + } + + if (Page* page = m_document.page()) + page->chrome().client().didInvalidateDocumentMarkerRects(); +} + +void DocumentMarkerController::invalidateRectsForMarkersInNode(Node& node) +{ + if (!hasMarkers()) + return; + + MarkerList* markers = m_markers.get(&node); + ASSERT(markers); + + for (auto& marker : *markers) + marker.invalidate(); + + if (Page* page = m_document.page()) + page->chrome().client().didInvalidateDocumentMarkerRects(); +} + +static void updateMainFrameLayoutIfNeeded(Document& document) +{ + Frame* frame = document.frame(); + if (!frame) + return; + + FrameView* mainFrameView = frame->mainFrame().view(); + if (!mainFrameView) + return; + + mainFrameView->updateLayoutAndStyleIfNeededRecursive(); +} + +void DocumentMarkerController::updateRectsForInvalidatedMarkersOfType(DocumentMarker::MarkerType markerType) +{ + if (!possiblyHasMarkers(markerType)) + return; + ASSERT(!(m_markers.isEmpty())); + + bool needsLayoutIfAnyRectsAreDirty = true; + + for (auto& nodeAndMarkers : m_markers) { + Node& node = *nodeAndMarkers.key; + for (auto& marker : *nodeAndMarkers.value) { + if (marker.type() != markerType) + continue; + + if (marker.isValid()) + continue; + + // We'll do up to one layout per call if we have any dirty markers. + if (needsLayoutIfAnyRectsAreDirty) { + updateMainFrameLayoutIfNeeded(m_document); + needsLayoutIfAnyRectsAreDirty = false; + } + + updateRenderedRectsForMarker(marker, node); + } + } +} + +Vector<FloatRect> DocumentMarkerController::renderedRectsForMarkers(DocumentMarker::MarkerType markerType) +{ + Vector<FloatRect> result; + + if (!possiblyHasMarkers(markerType)) + return result; + ASSERT(!(m_markers.isEmpty())); + + Frame* frame = m_document.frame(); + if (!frame) + return result; + FrameView* frameView = frame->view(); + if (!frameView) + return result; + + updateRectsForInvalidatedMarkersOfType(markerType); + + bool isSubframe = !frame->isMainFrame(); + IntRect subframeClipRect; + if (isSubframe) + subframeClipRect = frameView->windowToContents(frameView->windowClipRect()); + + for (auto& nodeAndMarkers : m_markers) { + Node& node = *nodeAndMarkers.key; + FloatRect overflowClipRect; + if (RenderObject* renderer = node.renderer()) + overflowClipRect = renderer->absoluteClippedOverflowRect(); + for (auto& marker : *nodeAndMarkers.value) { + if (marker.type() != markerType) + continue; + + auto renderedRects = marker.unclippedAbsoluteRects(); + + // Clip document markers by their overflow clip. + if (node.renderer()) { + for (auto& rect : renderedRects) + rect.intersect(overflowClipRect); + } + + // Clip subframe document markers by their frame. + if (isSubframe) { + for (auto& rect : renderedRects) + rect.intersect(subframeClipRect); + } + + for (const auto& rect : renderedRects) { + if (!rect.isEmpty()) + result.append(rect); + } + } } + + return result; } // Markers are stored in order sorted by their start offset. @@ -166,12 +302,20 @@ void DocumentMarkerController::addMarker(Node* node, const DocumentMarker& newMa if (newMarker.endOffset() == newMarker.startOffset()) return; + if (auto* renderer = node->renderer()) { + // FIXME: Factor the marker painting code out of InlineTextBox and teach simple line layout to use it. + if (is<RenderText>(*renderer)) + downcast<RenderText>(*renderer).ensureLineBoxes(); + else if (is<RenderBlockFlow>(*renderer)) + downcast<RenderBlockFlow>(*renderer).ensureLineBoxes(); + } + m_possiblyExistingMarkerTypes.add(newMarker.type()); - OwnPtr<MarkerList>& list = m_markers.add(node, nullptr).iterator->value; + std::unique_ptr<MarkerList>& list = m_markers.add(node, nullptr).iterator->value; if (!list) { - list = adoptPtr(new MarkerList); + list = std::make_unique<MarkerList>(); list->append(RenderedDocumentMarker(newMarker)); #if PLATFORM(IOS) } else if (newMarker.type() == DocumentMarker::DictationPhraseWithAlternatives || newMarker.type() == DocumentMarker::DictationResult) { @@ -225,9 +369,10 @@ void DocumentMarkerController::addMarker(Node* node, const DocumentMarker& newMa list->insert(i, RenderedDocumentMarker(toInsert)); } - // repaint the affected node if (node->renderer()) node->renderer()->repaint(); + + invalidateRectsForMarkersInNode(*node); } // copies markers from srcNode to dstNode, applying the specified shift delta to the copies. The shift is @@ -247,9 +392,7 @@ void DocumentMarkerController::copyMarkers(Node* srcNode, unsigned startOffset, bool docDirty = false; unsigned endOffset = startOffset + length - 1; - for (size_t i = 0; i != list->size(); ++i) { - DocumentMarker marker = list->at(i); - + for (auto& marker : *list) { // stop if we are now past the specified range if (marker.startOffset() > endOffset) break; @@ -269,7 +412,6 @@ void DocumentMarkerController::copyMarkers(Node* srcNode, unsigned startOffset, addMarker(dstNode, marker); } - // repaint the affected node if (docDirty && dstNode->renderer()) dstNode->renderer()->repaint(); } @@ -335,7 +477,6 @@ void DocumentMarkerController::removeMarkers(Node* node, unsigned startOffset, i m_possiblyExistingMarkerTypes = 0; } - // repaint the affected node if (docDirty && node->renderer()) node->renderer()->repaint(); } @@ -343,19 +484,13 @@ void DocumentMarkerController::removeMarkers(Node* node, unsigned startOffset, i DocumentMarker* DocumentMarkerController::markerContainingPoint(const LayoutPoint& point, DocumentMarker::MarkerType markerType) { if (!possiblyHasMarkers(markerType)) - return 0; + return nullptr; ASSERT(!(m_markers.isEmpty())); - // outer loop: process each node that contains any markers - MarkerMap::iterator end = m_markers.end(); - for (MarkerMap::iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) { - // inner loop; process each marker in this node - MarkerList* list = nodeIterator->value.get(); - unsigned markerCount = list->size(); - for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) { - RenderedDocumentMarker& marker = list->at(markerIndex); + updateRectsForInvalidatedMarkersOfType(markerType); - // skip marker that is wrong type + for (auto& nodeAndMarkers : m_markers) { + for (auto& marker : *nodeAndMarkers.value) { if (marker.type() != markerType) continue; @@ -364,47 +499,42 @@ DocumentMarker* DocumentMarkerController::markerContainingPoint(const LayoutPoin } } - return 0; + return nullptr; } -Vector<DocumentMarker*> DocumentMarkerController::markersFor(Node* node, DocumentMarker::MarkerTypes markerTypes) +Vector<RenderedDocumentMarker*> DocumentMarkerController::markersFor(Node* node, DocumentMarker::MarkerTypes markerTypes) { - Vector<DocumentMarker*> result; + Vector<RenderedDocumentMarker*> result; MarkerList* list = m_markers.get(node); if (!list) return result; - for (size_t i = 0; i < list->size(); ++i) { - if (markerTypes.contains(list->at(i).type())) - result.append(&(list->at(i))); + for (auto& marker : *list) { + if (markerTypes.contains(marker.type())) + result.append(&marker); } return result; } -Vector<DocumentMarker*> DocumentMarkerController::markersInRange(Range* range, DocumentMarker::MarkerTypes markerTypes) +Vector<RenderedDocumentMarker*> DocumentMarkerController::markersInRange(Range* range, DocumentMarker::MarkerTypes markerTypes) { if (!possiblyHasMarkers(markerTypes)) - return Vector<DocumentMarker*>(); + return Vector<RenderedDocumentMarker*>(); - Vector<DocumentMarker*> foundMarkers; + Vector<RenderedDocumentMarker*> foundMarkers; - Node* startContainer = range->startContainer(); - ASSERT(startContainer); - Node* endContainer = range->endContainer(); - ASSERT(endContainer); + Node& startContainer = range->startContainer(); + Node& endContainer = range->endContainer(); Node* pastLastNode = range->pastLastNode(); - for (Node* node = range->firstNode(); node != pastLastNode; node = NodeTraversal::next(node)) { - Vector<DocumentMarker*> markers = markersFor(node); - Vector<DocumentMarker*>::const_iterator end = markers.end(); - for (Vector<DocumentMarker*>::const_iterator it = markers.begin(); it != end; ++it) { - DocumentMarker* marker = *it; + for (Node* node = range->firstNode(); node != pastLastNode; node = NodeTraversal::next(*node)) { + for (auto* marker : markersFor(node)) { if (!markerTypes.contains(marker->type())) continue; - if (node == startContainer && marker->endOffset() <= static_cast<unsigned>(range->startOffset())) + if (node == &startContainer && marker->endOffset() <= range->startOffset()) continue; - if (node == endContainer && marker->startOffset() >= static_cast<unsigned>(range->endOffset())) + if (node == &endContainer && marker->startOffset() >= range->endOffset()) continue; foundMarkers.append(marker); } @@ -412,37 +542,6 @@ Vector<DocumentMarker*> DocumentMarkerController::markersInRange(Range* range, D return foundMarkers; } -Vector<IntRect> DocumentMarkerController::renderedRectsForMarkers(DocumentMarker::MarkerType markerType) -{ - Vector<IntRect> result; - - if (!possiblyHasMarkers(markerType)) - return result; - ASSERT(!(m_markers.isEmpty())); - - // outer loop: process each node - MarkerMap::iterator end = m_markers.end(); - for (MarkerMap::iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) { - // inner loop; process each marker in this node - MarkerList* list = nodeIterator->value.get(); - unsigned markerCount = list->size(); - for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) { - const RenderedDocumentMarker& marker = list->at(markerIndex); - - // skip marker that is wrong type - if (marker.type() != markerType) - continue; - - if (!marker.isRendered()) - continue; - - result.append(marker.renderedRect()); - } - } - - return result; -} - void DocumentMarkerController::removeMarkers(Node* node, DocumentMarker::MarkerTypes markerTypes) { if (!possiblyHasMarkers(markerTypes)) @@ -462,9 +561,8 @@ void DocumentMarkerController::removeMarkers(DocumentMarker::MarkerTypes markerT Vector<RefPtr<Node>> nodesWithMarkers; copyKeysToVector(m_markers, nodesWithMarkers); - unsigned size = nodesWithMarkers.size(); - for (unsigned i = 0; i < size; ++i) { - MarkerMap::iterator iterator = m_markers.find(nodesWithMarkers[i]); + for (auto& node : nodesWithMarkers) { + auto iterator = m_markers.find(node); if (iterator != m_markers.end()) removeMarkersFromList(iterator, markerTypes); } @@ -520,18 +618,13 @@ void DocumentMarkerController::repaintMarkers(DocumentMarker::MarkerTypes marker ASSERT(!m_markers.isEmpty()); // outer loop: process each markered node in the document - MarkerMap::iterator end = m_markers.end(); - for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) { - Node* node = i->key.get(); + for (auto& marker : m_markers) { + Node* node = marker.key.get(); // inner loop: process each marker in the current node - MarkerList* list = i->value.get(); bool nodeNeedsRepaint = false; - for (size_t i = 0; i != list->size(); ++i) { - DocumentMarker marker = list->at(i); - - // skip nodes that are not of the specified type - if (markerTypes.contains(marker.type())) { + for (auto& documentMarker : *marker.value) { + if (markerTypes.contains(documentMarker.type())) { nodeNeedsRepaint = true; break; } @@ -546,19 +639,6 @@ void DocumentMarkerController::repaintMarkers(DocumentMarker::MarkerTypes marker } } -void DocumentMarkerController::invalidateRenderedRectsForMarkersInRect(const LayoutRect& r) -{ - // outer loop: process each markered node in the document - MarkerMap::iterator end = m_markers.end(); - for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) { - - // inner loop: process each rect in the current node - MarkerList* list = i->value.get(); - for (size_t listIndex = 0; listIndex < list->size(); ++listIndex) - list->at(listIndex).invalidate(r); - } -} - void DocumentMarkerController::shiftMarkers(Node* node, unsigned startOffset, int delta) { if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) @@ -569,9 +649,10 @@ void DocumentMarkerController::shiftMarkers(Node* node, unsigned startOffset, in if (!list) return; - bool docDirty = false; + bool didShiftMarker = false; for (size_t i = 0; i != list->size(); ) { RenderedDocumentMarker& marker = list->at(i); + // FIXME: How can this possibly be iOS-specific code? #if PLATFORM(IOS) int targetStartOffset = marker.startOffset() + delta; int targetEndOffset = marker.endOffset() + delta; @@ -583,10 +664,7 @@ void DocumentMarkerController::shiftMarkers(Node* node, unsigned startOffset, in if (marker.startOffset() >= startOffset) { ASSERT((int)marker.startOffset() + delta >= 0); marker.shiftOffsets(delta); - docDirty = true; - - // Marker moved, so previously-computed rendered rectangle is now invalid - marker.invalidate(); + didShiftMarker = true; #if !PLATFORM(IOS) } #else @@ -598,18 +676,18 @@ void DocumentMarkerController::shiftMarkers(Node* node, unsigned startOffset, in continue; } marker.setEndOffset(targetEndOffset < node->maxCharacterOffset() ? targetEndOffset : node->maxCharacterOffset()); - docDirty = true; - - // Marker moved, so previously-computed rendered rectangle is now invalid - marker.invalidate(); + didShiftMarker = true; } #endif ++i; } - // repaint the affected node - if (docDirty && node->renderer()) - node->renderer()->repaint(); + if (didShiftMarker) { + invalidateRectsForMarkersInNode(*node); + + if (node->renderer()) + node->renderer()->repaint(); + } } void DocumentMarkerController::setMarkersActive(Range* range, bool active) @@ -618,14 +696,14 @@ void DocumentMarkerController::setMarkersActive(Range* range, bool active) return; ASSERT(!m_markers.isEmpty()); - Node* startContainer = range->startContainer(); - Node* endContainer = range->endContainer(); + Node& startContainer = range->startContainer(); + Node& endContainer = range->endContainer(); Node* pastLastNode = range->pastLastNode(); - for (Node* node = range->firstNode(); node != pastLastNode; node = NodeTraversal::next(node)) { - int startOffset = node == startContainer ? range->startOffset() : 0; - int endOffset = node == endContainer ? range->endOffset() : INT_MAX; + for (Node* node = range->firstNode(); node != pastLastNode; node = NodeTraversal::next(*node)) { + unsigned startOffset = node == &startContainer ? range->startOffset() : 0; + unsigned endOffset = node == &endContainer ? range->endOffset() : std::numeric_limits<unsigned>::max(); setMarkersActive(node, startOffset, endOffset, active); } } @@ -636,10 +714,8 @@ void DocumentMarkerController::setMarkersActive(Node* node, unsigned startOffset if (!list) return; - bool docDirty = false; - for (size_t i = 0; i != list->size(); ++i) { - DocumentMarker& marker = list->at(i); - + bool didActivateMarker = false; + for (auto& marker : *list) { // Markers are returned in order, so stop if we are now past the specified range. if (marker.startOffset() >= endOffset) break; @@ -649,11 +725,10 @@ void DocumentMarkerController::setMarkersActive(Node* node, unsigned startOffset continue; marker.setActiveMatch(active); - docDirty = true; + didActivateMarker = true; } - // repaint the affected node - if (docDirty && node->renderer()) + if (didActivateMarker && node->renderer()) node->renderer()->repaint(); } @@ -663,22 +738,17 @@ bool DocumentMarkerController::hasMarkers(Range* range, DocumentMarker::MarkerTy return false; ASSERT(!m_markers.isEmpty()); - Node* startContainer = range->startContainer(); - ASSERT(startContainer); - Node* endContainer = range->endContainer(); - ASSERT(endContainer); + Node& startContainer = range->startContainer(); + Node& endContainer = range->endContainer(); Node* pastLastNode = range->pastLastNode(); - for (Node* node = range->firstNode(); node != pastLastNode; node = NodeTraversal::next(node)) { - Vector<DocumentMarker*> markers = markersFor(node); - Vector<DocumentMarker*>::const_iterator end = markers.end(); - for (Vector<DocumentMarker*>::const_iterator it = markers.begin(); it != end; ++it) { - DocumentMarker* marker = *it; + for (Node* node = range->firstNode(); node != pastLastNode; node = NodeTraversal::next(*node)) { + for (auto* marker : markersFor(node)) { if (!markerTypes.contains(marker->type())) continue; - if (node == startContainer && marker->endOffset() <= static_cast<unsigned>(range->startOffset())) + if (node == &startContainer && marker->endOffset() <= static_cast<unsigned>(range->startOffset())) continue; - if (node == endContainer && marker->startOffset() >= static_cast<unsigned>(range->endOffset())) + if (node == &endContainer && marker->startOffset() >= static_cast<unsigned>(range->endOffset())) continue; return true; } @@ -692,13 +762,13 @@ void DocumentMarkerController::clearDescriptionOnMarkersIntersectingRange(Range* return; ASSERT(!m_markers.isEmpty()); - Node* startContainer = range->startContainer(); - Node* endContainer = range->endContainer(); + Node& startContainer = range->startContainer(); + Node& endContainer = range->endContainer(); Node* pastLastNode = range->pastLastNode(); - for (Node* node = range->firstNode(); node != pastLastNode; node = NodeTraversal::next(node)) { - unsigned startOffset = node == startContainer ? range->startOffset() : 0; - unsigned endOffset = node == endContainer ? static_cast<unsigned>(range->endOffset()) : std::numeric_limits<unsigned>::max(); + for (Node* node = range->firstNode(); node != pastLastNode; node = NodeTraversal::next(*node)) { + unsigned startOffset = node == &startContainer ? range->startOffset() : 0; + unsigned endOffset = node == &endContainer ? static_cast<unsigned>(range->endOffset()) : std::numeric_limits<unsigned>::max(); MarkerList* list = m_markers.get(node); if (!list) continue; @@ -716,24 +786,20 @@ void DocumentMarkerController::clearDescriptionOnMarkersIntersectingRange(Range* continue; } - marker.clearDetails(); + marker.clearData(); } } } -#ifndef NDEBUG +#if ENABLE(TREE_DEBUGGING) void DocumentMarkerController::showMarkers() const { fprintf(stderr, "%d nodes have markers:\n", m_markers.size()); - MarkerMap::const_iterator end = m_markers.end(); - for (MarkerMap::const_iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) { - Node* node = nodeIterator->key.get(); + for (auto& marker : m_markers) { + Node* node = marker.key.get(); fprintf(stderr, "%p", node); - MarkerList* list = nodeIterator->value.get(); - for (unsigned markerIndex = 0; markerIndex < list->size(); ++markerIndex) { - const DocumentMarker& marker = list->at(markerIndex); - fprintf(stderr, " %d:[%d:%d](%d)", marker.type(), marker.startOffset(), marker.endOffset(), marker.activeMatch()); - } + for (auto& documentMarker : *marker.value) + fprintf(stderr, " %d:[%d:%d](%d)", documentMarker.type(), documentMarker.startOffset(), documentMarker.endOffset(), documentMarker.isActiveMatch()); fprintf(stderr, "\n"); } @@ -742,7 +808,7 @@ void DocumentMarkerController::showMarkers() const } // namespace WebCore -#ifndef NDEBUG +#if ENABLE(TREE_DEBUGGING) void showDocumentMarkers(const WebCore::DocumentMarkerController* controller) { if (controller) diff --git a/Source/WebCore/dom/DocumentMarkerController.h b/Source/WebCore/dom/DocumentMarkerController.h index 76946478b..2c2cd377a 100644 --- a/Source/WebCore/dom/DocumentMarkerController.h +++ b/Source/WebCore/dom/DocumentMarkerController.h @@ -24,16 +24,17 @@ * */ -#ifndef DocumentMarkerController_h -#define DocumentMarkerController_h +#pragma once #include "DocumentMarker.h" #include "IntRect.h" +#include <memory> #include <wtf/HashMap.h> #include <wtf/Vector.h> namespace WebCore { +class Document; class LayoutPoint; class LayoutRect; class Node; @@ -44,17 +45,17 @@ class DocumentMarkerController { WTF_MAKE_NONCOPYABLE(DocumentMarkerController); WTF_MAKE_FAST_ALLOCATED; public: - DocumentMarkerController(); + DocumentMarkerController(Document&); ~DocumentMarkerController(); void detach(); void addMarker(Range*, DocumentMarker::MarkerType); void addMarker(Range*, DocumentMarker::MarkerType, const String& description); void addMarkerToNode(Node*, unsigned startOffset, unsigned length, DocumentMarker::MarkerType); - void addMarkerToNode(Node*, unsigned startOffset, unsigned length, DocumentMarker::MarkerType, PassRefPtr<DocumentMarkerDetails>); - void addTextMatchMarker(const Range*, bool activeMatch); + void addMarkerToNode(Node*, unsigned startOffset, unsigned length, DocumentMarker::MarkerType, DocumentMarker::Data&&); + WEBCORE_EXPORT void addTextMatchMarker(const Range*, bool activeMatch); #if PLATFORM(IOS) - void addMarker(Range*, DocumentMarker::MarkerType, String description, const Vector<String>& interpretations, const RetainPtr<id>& metadata); + void addMarker(Range*, DocumentMarker::MarkerType, const String& description, const Vector<String>& interpretations, const RetainPtr<id>& metadata); void addDictationPhraseWithAlternativesMarker(Range*, const Vector<String>& interpretations); void addDictationResultMarker(Range*, const RetainPtr<id>& metadata); #endif @@ -74,21 +75,26 @@ public: void removeMarkers(Range*, DocumentMarker::MarkerTypes = DocumentMarker::AllMarkers(), RemovePartiallyOverlappingMarkerOrNot = DoNotRemovePartiallyOverlappingMarker); void removeMarkers(Node*, unsigned startOffset, int length, DocumentMarker::MarkerTypes = DocumentMarker::AllMarkers(), RemovePartiallyOverlappingMarkerOrNot = DoNotRemovePartiallyOverlappingMarker); - void removeMarkers(DocumentMarker::MarkerTypes = DocumentMarker::AllMarkers()); + WEBCORE_EXPORT void removeMarkers(DocumentMarker::MarkerTypes = DocumentMarker::AllMarkers()); void removeMarkers(Node*, DocumentMarker::MarkerTypes = DocumentMarker::AllMarkers()); void repaintMarkers(DocumentMarker::MarkerTypes = DocumentMarker::AllMarkers()); - void invalidateRenderedRectsForMarkersInRect(const LayoutRect&); void shiftMarkers(Node*, unsigned startOffset, int delta); void setMarkersActive(Range*, bool); void setMarkersActive(Node*, unsigned startOffset, unsigned endOffset, bool); - DocumentMarker* markerContainingPoint(const LayoutPoint&, DocumentMarker::MarkerType); - Vector<DocumentMarker*> markersFor(Node*, DocumentMarker::MarkerTypes = DocumentMarker::AllMarkers()); - Vector<DocumentMarker*> markersInRange(Range*, DocumentMarker::MarkerTypes); - Vector<IntRect> renderedRectsForMarkers(DocumentMarker::MarkerType); + WEBCORE_EXPORT Vector<RenderedDocumentMarker*> markersFor(Node*, DocumentMarker::MarkerTypes = DocumentMarker::AllMarkers()); + WEBCORE_EXPORT Vector<RenderedDocumentMarker*> markersInRange(Range*, DocumentMarker::MarkerTypes); void clearDescriptionOnMarkersIntersectingRange(Range*, DocumentMarker::MarkerTypes); -#ifndef NDEBUG + WEBCORE_EXPORT void updateRectsForInvalidatedMarkersOfType(DocumentMarker::MarkerType); + + void invalidateRectsForAllMarkers(); + void invalidateRectsForMarkersInNode(Node&); + + DocumentMarker* markerContainingPoint(const LayoutPoint&, DocumentMarker::MarkerType); + WEBCORE_EXPORT Vector<FloatRect> renderedRectsForMarkers(DocumentMarker::MarkerType); + +#if ENABLE(TREE_DEBUGGING) void showMarkers() const; #endif @@ -96,19 +102,18 @@ private: void addMarker(Node*, const DocumentMarker&); typedef Vector<RenderedDocumentMarker> MarkerList; - typedef HashMap<RefPtr<Node>, OwnPtr<MarkerList>> MarkerMap; + typedef HashMap<RefPtr<Node>, std::unique_ptr<MarkerList>> MarkerMap; bool possiblyHasMarkers(DocumentMarker::MarkerTypes); void removeMarkersFromList(MarkerMap::iterator, DocumentMarker::MarkerTypes); MarkerMap m_markers; // Provide a quick way to determine whether a particular marker type is absent without going through the map. - DocumentMarker::MarkerTypes m_possiblyExistingMarkerTypes; + DocumentMarker::MarkerTypes m_possiblyExistingMarkerTypes { 0 }; + Document& m_document; }; } // namespace WebCore -#ifndef NDEBUG +#if ENABLE(TREE_DEBUGGING) void showDocumentMarkers(const WebCore::DocumentMarkerController*); #endif - -#endif // DocumentMarkerController_h diff --git a/Source/WebCore/dom/DocumentOrShadowRoot.idl b/Source/WebCore/dom/DocumentOrShadowRoot.idl new file mode 100644 index 000000000..82bb61701 --- /dev/null +++ b/Source/WebCore/dom/DocumentOrShadowRoot.idl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +// https://dom.spec.whatwg.org/#documentorshadowroot +// https://www.w3.org/TR/shadow-dom/#extensions-to-the-documentorshadowroot-mixin +[ + NoInterfaceObject, +] interface DocumentOrShadowRoot { + Element? elementFromPoint(double x, double y); + readonly attribute Element? activeElement; + [Conditional=POINTER_LOCK] readonly attribute Element? pointerLockElement; +}; diff --git a/Source/WebCore/dom/DocumentOrderedMap.cpp b/Source/WebCore/dom/DocumentOrderedMap.cpp index 1fab1d101..23c79406b 100644 --- a/Source/WebCore/dom/DocumentOrderedMap.cpp +++ b/Source/WebCore/dom/DocumentOrderedMap.cpp @@ -41,47 +41,6 @@ namespace WebCore { using namespace HTMLNames; -inline bool keyMatchesId(const AtomicStringImpl& key, const Element& element) -{ - return element.getIdAttribute().impl() == &key; -} - -inline bool keyMatchesName(const AtomicStringImpl& key, const Element& element) -{ - return element.getNameAttribute().impl() == &key; -} - -inline bool keyMatchesMapName(const AtomicStringImpl& key, const Element& element) -{ - return isHTMLMapElement(element) && toHTMLMapElement(element).getName().impl() == &key; -} - -inline bool keyMatchesLowercasedMapName(const AtomicStringImpl& key, const Element& element) -{ - return isHTMLMapElement(element) && toHTMLMapElement(element).getName().lower().impl() == &key; -} - -inline bool keyMatchesLowercasedUsemap(const AtomicStringImpl& key, const Element& element) -{ - // FIXME: HTML5 specification says we should match both image and object elements. - return isHTMLImageElement(element) && toHTMLImageElement(element).matchesLowercasedUsemap(key); -} - -inline bool keyMatchesLabelForAttribute(const AtomicStringImpl& key, const Element& element) -{ - return isHTMLLabelElement(element) && element.getAttribute(forAttr).impl() == &key; -} - -inline bool keyMatchesWindowNamedItem(const AtomicStringImpl& key, const Element& element) -{ - return WindowNameCollection::nodeMatches(const_cast<Element*>(&element), &key); -} - -inline bool keyMatchesDocumentNamedItem(const AtomicStringImpl& key, const Element& element) -{ - return DocumentNameCollection::nodeMatches(const_cast<Element*>(&element), &key); -} - void DocumentOrderedMap::clear() { m_map.clear(); @@ -89,15 +48,25 @@ void DocumentOrderedMap::clear() void DocumentOrderedMap::add(const AtomicStringImpl& key, Element& element, const TreeScope& treeScope) { + UNUSED_PARAM(treeScope); ASSERT_WITH_SECURITY_IMPLICATION(element.isInTreeScope()); - ASSERT_WITH_SECURITY_IMPLICATION(treeScope.rootNode()->containsIncludingShadowDOM(&element)); - if (!element.isInTreeScope() || &element.document() != treeScope.documentScope()) + ASSERT_WITH_SECURITY_IMPLICATION(treeScope.rootNode().containsIncludingShadowDOM(&element)); + + if (!element.isInTreeScope()) return; - Map::AddResult addResult = m_map.add(&key, MapEntry(&element)); + Map::AddResult addResult = m_map.ensure(&key, [&element] { + return MapEntry(&element); + }); + MapEntry& entry = addResult.iterator->value; + +#if !ASSERT_DISABLED || ENABLE(SECURITY_ASSERTIONS) + ASSERT_WITH_SECURITY_IMPLICATION(!entry.registeredElements.contains(&element)); + entry.registeredElements.add(&element); +#endif + if (addResult.isNewEntry) return; - MapEntry& entry = addResult.iterator->value; ASSERT_WITH_SECURITY_IMPLICATION(entry.count); entry.element = nullptr; entry.count++; @@ -108,11 +77,13 @@ void DocumentOrderedMap::remove(const AtomicStringImpl& key, Element& element) { m_map.checkConsistency(); auto it = m_map.find(&key); + ASSERT_WITH_SECURITY_IMPLICATION(it != m_map.end()); if (it == m_map.end()) return; - MapEntry& entry = it->value; + MapEntry& entry = it->value; + ASSERT_WITH_SECURITY_IMPLICATION(entry.registeredElements.remove(&element)); ASSERT_WITH_SECURITY_IMPLICATION(entry.count); if (entry.count == 1) { ASSERT_WITH_SECURITY_IMPLICATION(!entry.element || entry.element == &element); @@ -125,8 +96,8 @@ void DocumentOrderedMap::remove(const AtomicStringImpl& key, Element& element) } } -template<bool keyMatches(const AtomicStringImpl&, const Element&)> -inline Element* DocumentOrderedMap::get(const AtomicStringImpl& key, const TreeScope& scope) const +template <typename KeyMatchingFunction> +inline Element* DocumentOrderedMap::get(const AtomicStringImpl& key, const TreeScope& scope, const KeyMatchingFunction& keyMatches) const { m_map.checkConsistency(); @@ -139,16 +110,18 @@ inline Element* DocumentOrderedMap::get(const AtomicStringImpl& key, const TreeS if (entry.element) { ASSERT_WITH_SECURITY_IMPLICATION(entry.element->isInTreeScope()); ASSERT_WITH_SECURITY_IMPLICATION(&entry.element->treeScope() == &scope); + ASSERT_WITH_SECURITY_IMPLICATION(entry.registeredElements.contains(entry.element)); return entry.element; } // We know there's at least one node that matches; iterate to find the first one. - for (auto& element : descendantsOfType<Element>(*scope.rootNode())) { + for (auto& element : descendantsOfType<Element>(scope.rootNode())) { if (!keyMatches(key, element)) continue; entry.element = &element; ASSERT_WITH_SECURITY_IMPLICATION(element.isInTreeScope()); ASSERT_WITH_SECURITY_IMPLICATION(&element.treeScope() == &scope); + ASSERT_WITH_SECURITY_IMPLICATION(entry.registeredElements.contains(entry.element)); return &element; } ASSERT_NOT_REACHED(); @@ -157,42 +130,52 @@ inline Element* DocumentOrderedMap::get(const AtomicStringImpl& key, const TreeS Element* DocumentOrderedMap::getElementById(const AtomicStringImpl& key, const TreeScope& scope) const { - return get<keyMatchesId>(key, scope); + return get(key, scope, [] (const AtomicStringImpl& key, const Element& element) { + return element.getIdAttribute().impl() == &key; + }); } Element* DocumentOrderedMap::getElementByName(const AtomicStringImpl& key, const TreeScope& scope) const { - return get<keyMatchesName>(key, scope); + return get(key, scope, [] (const AtomicStringImpl& key, const Element& element) { + return element.getNameAttribute().impl() == &key; + }); } HTMLMapElement* DocumentOrderedMap::getElementByMapName(const AtomicStringImpl& key, const TreeScope& scope) const { - return toHTMLMapElement(get<keyMatchesMapName>(key, scope)); -} - -HTMLMapElement* DocumentOrderedMap::getElementByLowercasedMapName(const AtomicStringImpl& key, const TreeScope& scope) const -{ - return toHTMLMapElement(get<keyMatchesLowercasedMapName>(key, scope)); + return downcast<HTMLMapElement>(get(key, scope, [] (const AtomicStringImpl& key, const Element& element) { + return is<HTMLMapElement>(element) && downcast<HTMLMapElement>(element).getName().impl() == &key; + })); } -HTMLImageElement* DocumentOrderedMap::getElementByLowercasedUsemap(const AtomicStringImpl& key, const TreeScope& scope) const +HTMLImageElement* DocumentOrderedMap::getElementByUsemap(const AtomicStringImpl& key, const TreeScope& scope) const { - return toHTMLImageElement(get<keyMatchesLowercasedUsemap>(key, scope)); + return downcast<HTMLImageElement>(get(key, scope, [] (const AtomicStringImpl& key, const Element& element) { + // FIXME: HTML5 specification says we should match both image and object elements. + return is<HTMLImageElement>(element) && downcast<HTMLImageElement>(element).matchesUsemap(key); + })); } HTMLLabelElement* DocumentOrderedMap::getElementByLabelForAttribute(const AtomicStringImpl& key, const TreeScope& scope) const { - return toHTMLLabelElement(get<keyMatchesLabelForAttribute>(key, scope)); + return downcast<HTMLLabelElement>(get(key, scope, [] (const AtomicStringImpl& key, const Element& element) { + return is<HTMLLabelElement>(element) && element.attributeWithoutSynchronization(forAttr).impl() == &key; + })); } Element* DocumentOrderedMap::getElementByWindowNamedItem(const AtomicStringImpl& key, const TreeScope& scope) const { - return get<keyMatchesWindowNamedItem>(key, scope); + return get(key, scope, [] (const AtomicStringImpl& key, const Element& element) { + return WindowNameCollection::elementMatches(element, &key); + }); } Element* DocumentOrderedMap::getElementByDocumentNamedItem(const AtomicStringImpl& key, const TreeScope& scope) const { - return get<keyMatchesDocumentNamedItem>(key, scope); + return get(key, scope, [] (const AtomicStringImpl& key, const Element& element) { + return DocumentNameCollection::elementMatches(element, &key); + }); } const Vector<Element*>* DocumentOrderedMap::getAllElementsById(const AtomicStringImpl& key, const TreeScope& scope) const @@ -210,12 +193,12 @@ const Vector<Element*>* DocumentOrderedMap::getAllElementsById(const AtomicStrin if (entry.orderedList.isEmpty()) { entry.orderedList.reserveCapacity(entry.count); - auto elementDescandents = descendantsOfType<Element>(*scope.rootNode()); + auto elementDescandents = descendantsOfType<Element>(scope.rootNode()); auto it = entry.element ? elementDescandents.beginAt(*entry.element) : elementDescandents.begin(); auto end = elementDescandents.end(); for (; it != end; ++it) { auto& element = *it; - if (!keyMatchesId(key, element)) + if (element.getIdAttribute().impl() != &key) continue; entry.orderedList.append(&element); } diff --git a/Source/WebCore/dom/DocumentOrderedMap.h b/Source/WebCore/dom/DocumentOrderedMap.h index 221297bdd..46a5106ef 100644 --- a/Source/WebCore/dom/DocumentOrderedMap.h +++ b/Source/WebCore/dom/DocumentOrderedMap.h @@ -28,11 +28,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DocumentOrderedMap_h -#define DocumentOrderedMap_h +#pragma once -#include <wtf/HashCountedSet.h> #include <wtf/HashMap.h> +#include <wtf/HashSet.h> #include <wtf/Vector.h> #include <wtf/text/AtomicStringImpl.h> @@ -45,6 +44,7 @@ class HTMLMapElement; class TreeScope; class DocumentOrderedMap { + WTF_MAKE_FAST_ALLOCATED; public: void add(const AtomicStringImpl&, Element&, const TreeScope&); void remove(const AtomicStringImpl&, Element&); @@ -58,8 +58,7 @@ public: Element* getElementById(const AtomicStringImpl&, const TreeScope&) const; Element* getElementByName(const AtomicStringImpl&, const TreeScope&) const; HTMLMapElement* getElementByMapName(const AtomicStringImpl&, const TreeScope&) const; - HTMLMapElement* getElementByLowercasedMapName(const AtomicStringImpl&, const TreeScope&) const; - HTMLImageElement* getElementByLowercasedUsemap(const AtomicStringImpl&, const TreeScope&) const; + HTMLImageElement* getElementByUsemap(const AtomicStringImpl&, const TreeScope&) const; HTMLLabelElement* getElementByLabelForAttribute(const AtomicStringImpl&, const TreeScope&) const; Element* getElementByWindowNamedItem(const AtomicStringImpl&, const TreeScope&) const; Element* getElementByDocumentNamedItem(const AtomicStringImpl&, const TreeScope&) const; @@ -67,21 +66,22 @@ public: const Vector<Element*>* getAllElementsById(const AtomicStringImpl&, const TreeScope&) const; private: - template<bool keyMatches(const AtomicStringImpl&, const Element&)> Element* get(const AtomicStringImpl&, const TreeScope&) const; + template <typename KeyMatchingFunction> + Element* get(const AtomicStringImpl&, const TreeScope&, const KeyMatchingFunction&) const; struct MapEntry { - MapEntry() - : element(0) - , count(0) - { } + MapEntry() { } explicit MapEntry(Element* firstElement) : element(firstElement) , count(1) { } - Element* element; - unsigned count; + Element* element { nullptr }; + unsigned count { 0 }; Vector<Element*> orderedList; +#if !ASSERT_DISABLED || ENABLE(SECURITY_ASSERTIONS) + HashSet<Element*> registeredElements; +#endif }; typedef HashMap<const AtomicStringImpl*, MapEntry> Map; @@ -107,5 +107,3 @@ inline bool DocumentOrderedMap::containsMultiple(const AtomicStringImpl& id) con } } // namespace WebCore - -#endif // DocumentOrderedMap_h diff --git a/Source/WebCore/dom/DocumentParser.cpp b/Source/WebCore/dom/DocumentParser.cpp index 9dca08fe2..6bcfb007c 100644 --- a/Source/WebCore/dom/DocumentParser.cpp +++ b/Source/WebCore/dom/DocumentParser.cpp @@ -64,7 +64,7 @@ void DocumentParser::stopParsing() void DocumentParser::detach() { m_state = DetachedState; - m_document = 0; + m_document = nullptr; } void DocumentParser::suspendScheduledTasks() diff --git a/Source/WebCore/dom/DocumentParser.h b/Source/WebCore/dom/DocumentParser.h index 7b2f0178e..cac337d72 100644 --- a/Source/WebCore/dom/DocumentParser.h +++ b/Source/WebCore/dom/DocumentParser.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2000 Peter Kelly (pmk@post.com) - * Copyright (C) 2005, 2006 Apple Computer, Inc. + * Copyright (C) 2005, 2006 Apple Inc. * Copyright (C) 2007 Samuel Weinig (sam@webkit.org) * Copyright (C) 2010 Google, Inc. * @@ -21,8 +21,7 @@ * */ -#ifndef DocumentParser_h -#define DocumentParser_h +#pragma once #include <wtf/Forward.h> #include <wtf/RefCounted.h> @@ -44,16 +43,16 @@ public: virtual bool hasInsertionPoint() { return true; } // insert is used by document.write. - virtual void insert(const SegmentedString&) = 0; + virtual void insert(SegmentedString&&) = 0; // appendBytes and flush are used by DocumentWriter (the loader). virtual void appendBytes(DocumentWriter&, const char* bytes, size_t length) = 0; virtual void flush(DocumentWriter&) = 0; // FIXME: append() should be private, but DocumentWriter::replaceDocument uses it for now. - // FIXME: This really should take a PassOwnPtr to signify that it expects to take - // ownership of the buffer. The parser expects the PassRefPtr to hold the only ref of the StringImpl. - virtual void append(PassRefPtr<StringImpl>) = 0; + // FIXME: This really should take a std::unique_ptr to signify that it expects to take + // ownership of the buffer. The parser expects the RefPtr to hold the only ref of the StringImpl. + virtual void append(RefPtr<StringImpl>&&) = 0; virtual void finish() = 0; @@ -116,5 +115,3 @@ private: }; } // namespace WebCore - -#endif // DocumentParser_h diff --git a/Source/WebCore/dom/DocumentSharedObjectPool.cpp b/Source/WebCore/dom/DocumentSharedObjectPool.cpp index 2cf7aeab3..b5f721cae 100644 --- a/Source/WebCore/dom/DocumentSharedObjectPool.cpp +++ b/Source/WebCore/dom/DocumentSharedObjectPool.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -43,7 +43,7 @@ inline bool hasSameAttributes(const Vector<Attribute>& attributes, ShareableElem return !memcmp(attributes.data(), elementData.m_attributeArray, attributes.size() * sizeof(Attribute)); } -PassRef<ShareableElementData> DocumentSharedObjectPool::cachedShareableElementDataWithAttributes(const Vector<Attribute>& attributes) +Ref<ShareableElementData> DocumentSharedObjectPool::cachedShareableElementDataWithAttributes(const Vector<Attribute>& attributes) { ASSERT(!attributes.isEmpty()); @@ -59,12 +59,4 @@ PassRef<ShareableElementData> DocumentSharedObjectPool::cachedShareableElementDa return *cachedData; } -DocumentSharedObjectPool::DocumentSharedObjectPool() -{ -} - -DocumentSharedObjectPool::~DocumentSharedObjectPool() -{ -} - } diff --git a/Source/WebCore/dom/DocumentSharedObjectPool.h b/Source/WebCore/dom/DocumentSharedObjectPool.h index 21ca8f5a6..22fccba24 100644 --- a/Source/WebCore/dom/DocumentSharedObjectPool.h +++ b/Source/WebCore/dom/DocumentSharedObjectPool.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,11 +24,10 @@ * */ -#ifndef DocumentSharedObjectPool_h -#define DocumentSharedObjectPool_h +#pragma once +#include <memory> #include <wtf/HashMap.h> -#include <wtf/PassOwnPtr.h> #include <wtf/RefPtr.h> #include <wtf/text/StringHash.h> @@ -38,19 +37,13 @@ class Attribute; class ShareableElementData; class DocumentSharedObjectPool { + WTF_MAKE_FAST_ALLOCATED; public: - static PassOwnPtr<DocumentSharedObjectPool> create() { return adoptPtr(new DocumentSharedObjectPool); } - ~DocumentSharedObjectPool(); - - PassRef<ShareableElementData> cachedShareableElementDataWithAttributes(const Vector<Attribute>&); + Ref<ShareableElementData> cachedShareableElementDataWithAttributes(const Vector<Attribute>&); private: - DocumentSharedObjectPool(); - typedef HashMap<unsigned, RefPtr<ShareableElementData>, AlreadyHashed> ShareableElementDataCache; ShareableElementDataCache m_shareableElementDataCache; }; -} - -#endif +} // namespace WebCore diff --git a/Source/WebCore/dom/DocumentStyleSheetCollection.cpp b/Source/WebCore/dom/DocumentStyleSheetCollection.cpp deleted file mode 100644 index a83731d1b..000000000 --- a/Source/WebCore/dom/DocumentStyleSheetCollection.cpp +++ /dev/null @@ -1,520 +0,0 @@ -/* - * Copyright (C) 1999 Lars Knoll (knoll@kde.org) - * (C) 1999 Antti Koivisto (koivisto@kde.org) - * (C) 2001 Dirk Mueller (mueller@kde.org) - * (C) 2006 Alexey Proskuryakov (ap@webkit.org) - * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2011, 2012 Apple Inc. All rights reserved. - * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) - * Copyright (C) 2008, 2009, 2011, 2012 Google Inc. All rights reserved. - * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) - * Copyright (C) Research In Motion Limited 2010-2011. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "config.h" -#include "DocumentStyleSheetCollection.h" - -#include "CSSStyleSheet.h" -#include "Element.h" -#include "HTMLIFrameElement.h" -#include "HTMLLinkElement.h" -#include "HTMLStyleElement.h" -#include "Page.h" -#include "PageGroup.h" -#include "ProcessingInstruction.h" -#include "SVGNames.h" -#include "SVGStyleElement.h" -#include "Settings.h" -#include "StyleInvalidationAnalysis.h" -#include "StyleResolver.h" -#include "StyleSheetContents.h" -#include "StyleSheetList.h" -#include "UserContentController.h" -#include "UserContentURLPattern.h" - -namespace WebCore { - -using namespace HTMLNames; - -DocumentStyleSheetCollection::DocumentStyleSheetCollection(Document& document) - : m_document(document) - , m_pendingStylesheets(0) - , m_injectedStyleSheetCacheValid(false) - , m_hadActiveLoadingStylesheet(false) - , m_pendingUpdateType(NoUpdate) - , m_usesSiblingRules(false) - , m_usesSiblingRulesOverride(false) - , m_usesFirstLineRules(false) - , m_usesFirstLetterRules(false) - , m_usesBeforeAfterRules(false) - , m_usesBeforeAfterRulesOverride(false) - , m_usesRemUnits(false) -{ -} - -void DocumentStyleSheetCollection::combineCSSFeatureFlags() -{ - // Delay resetting the flags until after next style recalc since unapplying the style may not work without these set (this is true at least with before/after). - const StyleResolver& styleResolver = m_document.ensureStyleResolver(); - m_usesSiblingRules = m_usesSiblingRules || styleResolver.usesSiblingRules(); - m_usesFirstLineRules = m_usesFirstLineRules || styleResolver.usesFirstLineRules(); - m_usesBeforeAfterRules = m_usesBeforeAfterRules || styleResolver.usesBeforeAfterRules(); -} - -void DocumentStyleSheetCollection::resetCSSFeatureFlags() -{ - const StyleResolver& styleResolver = m_document.ensureStyleResolver(); - m_usesSiblingRules = styleResolver.usesSiblingRules(); - m_usesFirstLineRules = styleResolver.usesFirstLineRules(); - m_usesBeforeAfterRules = styleResolver.usesBeforeAfterRules(); -} - -CSSStyleSheet* DocumentStyleSheetCollection::pageUserSheet() -{ - if (m_pageUserSheet) - return m_pageUserSheet.get(); - - Page* owningPage = m_document.page(); - if (!owningPage) - return 0; - - String userSheetText = owningPage->userStyleSheet(); - if (userSheetText.isEmpty()) - return 0; - - // Parse the sheet and cache it. - m_pageUserSheet = CSSStyleSheet::createInline(m_document, m_document.settings()->userStyleSheetLocation()); - m_pageUserSheet->contents().setIsUserStyleSheet(true); - m_pageUserSheet->contents().parseString(userSheetText); - return m_pageUserSheet.get(); -} - -void DocumentStyleSheetCollection::clearPageUserSheet() -{ - if (m_pageUserSheet) { - m_pageUserSheet = 0; - m_document.styleResolverChanged(DeferRecalcStyle); - } -} - -void DocumentStyleSheetCollection::updatePageUserSheet() -{ - clearPageUserSheet(); - if (pageUserSheet()) - m_document.styleResolverChanged(RecalcStyleImmediately); -} - -const Vector<RefPtr<CSSStyleSheet>>& DocumentStyleSheetCollection::injectedUserStyleSheets() const -{ - updateInjectedStyleSheetCache(); - return m_injectedUserStyleSheets; -} - -const Vector<RefPtr<CSSStyleSheet>>& DocumentStyleSheetCollection::injectedAuthorStyleSheets() const -{ - updateInjectedStyleSheetCache(); - return m_injectedAuthorStyleSheets; -} - -void DocumentStyleSheetCollection::updateInjectedStyleSheetCache() const -{ - if (m_injectedStyleSheetCacheValid) - return; - m_injectedStyleSheetCacheValid = true; - m_injectedUserStyleSheets.clear(); - m_injectedAuthorStyleSheets.clear(); - - Page* owningPage = m_document.page(); - if (!owningPage) - return; - - const auto* userContentController = owningPage->userContentController(); - if (!userContentController) - return; - - const UserStyleSheetMap* userStyleSheets = userContentController->userStyleSheets(); - if (!userStyleSheets) - return; - - for (auto& styleSheets : userStyleSheets->values()) { - for (const auto& sheet : *styleSheets) { - if (sheet->injectedFrames() == InjectInTopFrameOnly && m_document.ownerElement()) - continue; - - if (!UserContentURLPattern::matchesPatterns(m_document.url(), sheet->whitelist(), sheet->blacklist())) - continue; - - RefPtr<CSSStyleSheet> groupSheet = CSSStyleSheet::createInline(const_cast<Document&>(m_document), sheet->url()); - bool isUserStyleSheet = sheet->level() == UserStyleUserLevel; - if (isUserStyleSheet) - m_injectedUserStyleSheets.append(groupSheet); - else - m_injectedAuthorStyleSheets.append(groupSheet); - - groupSheet->contents().setIsUserStyleSheet(isUserStyleSheet); - groupSheet->contents().parseString(sheet->source()); - } - } -} - -void DocumentStyleSheetCollection::invalidateInjectedStyleSheetCache() -{ - if (!m_injectedStyleSheetCacheValid) - return; - m_injectedStyleSheetCacheValid = false; - if (m_injectedUserStyleSheets.isEmpty() && m_injectedAuthorStyleSheets.isEmpty()) - return; - m_document.styleResolverChanged(DeferRecalcStyle); -} - -void DocumentStyleSheetCollection::addAuthorSheet(PassRef<StyleSheetContents> authorSheet) -{ - ASSERT(!authorSheet.get().isUserStyleSheet()); - m_authorStyleSheets.append(CSSStyleSheet::create(std::move(authorSheet), &m_document)); - m_document.styleResolverChanged(RecalcStyleImmediately); -} - -void DocumentStyleSheetCollection::addUserSheet(PassRef<StyleSheetContents> userSheet) -{ - ASSERT(userSheet.get().isUserStyleSheet()); - m_userStyleSheets.append(CSSStyleSheet::create(std::move(userSheet), &m_document)); - m_document.styleResolverChanged(RecalcStyleImmediately); -} - -// This method is called whenever a top-level stylesheet has finished loading. -void DocumentStyleSheetCollection::removePendingSheet(RemovePendingSheetNotificationType notification) -{ - // Make sure we knew this sheet was pending, and that our count isn't out of sync. - ASSERT(m_pendingStylesheets > 0); - - m_pendingStylesheets--; - -#ifdef INSTRUMENT_LAYOUT_SCHEDULING - if (!ownerElement()) - printf("Stylesheet loaded at time %d. %d stylesheets still remain.\n", elapsedTime(), m_pendingStylesheets); -#endif - - if (m_pendingStylesheets) - return; - - if (notification == RemovePendingSheetNotifyLater) { - m_document.setNeedsNotifyRemoveAllPendingStylesheet(); - return; - } - - m_document.didRemoveAllPendingStylesheet(); -} - -void DocumentStyleSheetCollection::addStyleSheetCandidateNode(Node& node, bool createdByParser) -{ - if (!node.inDocument()) - return; - - // Until the <body> exists, we have no choice but to compare document positions, - // since styles outside of the body and head continue to be shunted into the head - // (and thus can shift to end up before dynamically added DOM content that is also - // outside the body). - if ((createdByParser && m_document.body()) || m_styleSheetCandidateNodes.isEmpty()) { - m_styleSheetCandidateNodes.add(&node); - return; - } - - // Determine an appropriate insertion point. - StyleSheetCandidateListHashSet::iterator begin = m_styleSheetCandidateNodes.begin(); - StyleSheetCandidateListHashSet::iterator end = m_styleSheetCandidateNodes.end(); - StyleSheetCandidateListHashSet::iterator it = end; - Node* followingNode = 0; - do { - --it; - Node* n = *it; - unsigned short position = n->compareDocumentPosition(&node); - if (position == Node::DOCUMENT_POSITION_FOLLOWING) { - m_styleSheetCandidateNodes.insertBefore(followingNode, &node); - return; - } - followingNode = n; - } while (it != begin); - - m_styleSheetCandidateNodes.insertBefore(followingNode, &node); -} - -void DocumentStyleSheetCollection::removeStyleSheetCandidateNode(Node& node) -{ - m_styleSheetCandidateNodes.remove(&node); -} - -void DocumentStyleSheetCollection::collectActiveStyleSheets(Vector<RefPtr<StyleSheet>>& sheets) -{ - if (m_document.settings() && !m_document.settings()->authorAndUserStylesEnabled()) - return; - - StyleSheetCandidateListHashSet::iterator begin = m_styleSheetCandidateNodes.begin(); - StyleSheetCandidateListHashSet::iterator end = m_styleSheetCandidateNodes.end(); - for (StyleSheetCandidateListHashSet::iterator it = begin; it != end; ++it) { - Node* n = *it; - StyleSheet* sheet = 0; - if (n->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) { - // Processing instruction (XML documents only). - // We don't support linking to embedded CSS stylesheets, see <https://bugs.webkit.org/show_bug.cgi?id=49281> for discussion. - ProcessingInstruction* pi = toProcessingInstruction(n); - sheet = pi->sheet(); -#if ENABLE(XSLT) - // Don't apply XSL transforms to already transformed documents -- <rdar://problem/4132806> - if (pi->isXSL() && !m_document.transformSourceDocument()) { - // Don't apply XSL transforms until loading is finished. - if (!m_document.parsing()) - m_document.applyXSLTransform(pi); - return; - } -#endif - } else if ((n->isHTMLElement() && (n->hasTagName(linkTag) || n->hasTagName(styleTag))) -#if ENABLE(SVG) - || (n->isSVGElement() && n->hasTagName(SVGNames::styleTag)) -#endif - ) { - Element* e = toElement(n); - AtomicString title = e->getAttribute(titleAttr); - bool enabledViaScript = false; - if (e->hasTagName(linkTag)) { - // <LINK> element - HTMLLinkElement* linkElement = toHTMLLinkElement(n); - if (linkElement->isDisabled()) - continue; - enabledViaScript = linkElement->isEnabledViaScript(); - if (linkElement->styleSheetIsLoading()) { - // it is loading but we should still decide which style sheet set to use - if (!enabledViaScript && !title.isEmpty() && m_preferredStylesheetSetName.isEmpty()) { - const AtomicString& rel = e->getAttribute(relAttr); - if (!rel.contains("alternate")) { - m_preferredStylesheetSetName = title; - m_selectedStylesheetSetName = title; - } - } - continue; - } - if (!linkElement->sheet()) - title = nullAtom; - } - // Get the current preferred styleset. This is the - // set of sheets that will be enabled. -#if ENABLE(SVG) - if (isSVGStyleElement(e)) - sheet = toSVGStyleElement(e)->sheet(); - else -#endif - { - if (isHTMLLinkElement(e)) - sheet = toHTMLLinkElement(n)->sheet(); - else { - // <STYLE> element - sheet = toHTMLStyleElement(e)->sheet(); - } - } - // Check to see if this sheet belongs to a styleset - // (thus making it PREFERRED or ALTERNATE rather than - // PERSISTENT). - AtomicString rel = e->getAttribute(relAttr); - if (!enabledViaScript && !title.isEmpty()) { - // Yes, we have a title. - if (m_preferredStylesheetSetName.isEmpty()) { - // No preferred set has been established. If - // we are NOT an alternate sheet, then establish - // us as the preferred set. Otherwise, just ignore - // this sheet. - if (e->hasTagName(styleTag) || !rel.contains("alternate")) - m_preferredStylesheetSetName = m_selectedStylesheetSetName = title; - } - if (title != m_preferredStylesheetSetName) - sheet = 0; - } - - if (rel.contains("alternate") && title.isEmpty()) - sheet = 0; - } - if (sheet) - sheets.append(sheet); - } -} - -void DocumentStyleSheetCollection::analyzeStyleSheetChange(UpdateFlag updateFlag, const Vector<RefPtr<CSSStyleSheet>>& newStylesheets, StyleResolverUpdateType& styleResolverUpdateType, bool& requiresFullStyleRecalc) -{ - styleResolverUpdateType = Reconstruct; - requiresFullStyleRecalc = true; - - // Stylesheets of <style> elements that @import stylesheets are active but loading. We need to trigger a full recalc when such loads are done. - bool hasActiveLoadingStylesheet = false; - unsigned newStylesheetCount = newStylesheets.size(); - for (unsigned i = 0; i < newStylesheetCount; ++i) { - if (newStylesheets[i]->isLoading()) - hasActiveLoadingStylesheet = true; - } - if (m_hadActiveLoadingStylesheet && !hasActiveLoadingStylesheet) { - m_hadActiveLoadingStylesheet = false; - return; - } - m_hadActiveLoadingStylesheet = hasActiveLoadingStylesheet; - - if (updateFlag != OptimizedUpdate) - return; - if (!m_document.styleResolverIfExists()) - return; - - // Find out which stylesheets are new. - unsigned oldStylesheetCount = m_activeAuthorStyleSheets.size(); - if (newStylesheetCount < oldStylesheetCount) - return; - Vector<StyleSheetContents*> addedSheets; - unsigned newIndex = 0; - for (unsigned oldIndex = 0; oldIndex < oldStylesheetCount; ++oldIndex) { - if (newIndex >= newStylesheetCount) - return; - while (m_activeAuthorStyleSheets[oldIndex] != newStylesheets[newIndex]) { - addedSheets.append(&newStylesheets[newIndex]->contents()); - ++newIndex; - if (newIndex == newStylesheetCount) - return; - } - ++newIndex; - } - bool hasInsertions = !addedSheets.isEmpty(); - while (newIndex < newStylesheetCount) { - addedSheets.append(&newStylesheets[newIndex]->contents()); - ++newIndex; - } - // If all new sheets were added at the end of the list we can just add them to existing StyleResolver. - // If there were insertions we need to re-add all the stylesheets so rules are ordered correctly. - styleResolverUpdateType = hasInsertions ? Reset : Additive; - - // If we are already parsing the body and so may have significant amount of elements, put some effort into trying to avoid style recalcs. - if (!m_document.body() || m_document.hasNodesWithPlaceholderStyle()) - return; - StyleInvalidationAnalysis invalidationAnalysis(addedSheets); - if (invalidationAnalysis.dirtiesAllStyle()) - return; - invalidationAnalysis.invalidateStyle(m_document); - requiresFullStyleRecalc = false; -} - -static bool styleSheetsUseRemUnits(const Vector<RefPtr<CSSStyleSheet>>& sheets) -{ - for (unsigned i = 0; i < sheets.size(); ++i) { - if (sheets[i]->contents().usesRemUnits()) - return true; - } - return false; -} - -static void filterEnabledNonemptyCSSStyleSheets(Vector<RefPtr<CSSStyleSheet>>& result, const Vector<RefPtr<StyleSheet>>& sheets) -{ - for (unsigned i = 0; i < sheets.size(); ++i) { - if (!sheets[i]->isCSSStyleSheet()) - continue; - if (sheets[i]->disabled()) - continue; - CSSStyleSheet* sheet = static_cast<CSSStyleSheet*>(sheets[i].get()); - if (!sheet->length()) - continue; - result.append(sheet); - } -} - -static void collectActiveCSSStyleSheetsFromSeamlessParents(Vector<RefPtr<CSSStyleSheet>>& sheets, Document& document) -{ - HTMLIFrameElement* seamlessParentIFrame = document.seamlessParentIFrame(); - if (!seamlessParentIFrame) - return; - sheets.appendVector(seamlessParentIFrame->document().styleSheetCollection().activeAuthorStyleSheets()); -} - -bool DocumentStyleSheetCollection::updateActiveStyleSheets(UpdateFlag updateFlag) -{ - if (m_document.inStyleRecalc()) { - // SVG <use> element may manage to invalidate style selector in the middle of a style recalc. - // https://bugs.webkit.org/show_bug.cgi?id=54344 - // FIXME: This should be fixed in SVG and the call site replaced by ASSERT(!m_inStyleRecalc). - m_pendingUpdateType = FullUpdate; - m_document.scheduleForcedStyleRecalc(); - return false; - - } - if (!m_document.hasLivingRenderTree()) - return false; - - Vector<RefPtr<StyleSheet>> activeStyleSheets; - collectActiveStyleSheets(activeStyleSheets); - - Vector<RefPtr<CSSStyleSheet>> activeCSSStyleSheets; - activeCSSStyleSheets.appendVector(injectedAuthorStyleSheets()); - activeCSSStyleSheets.appendVector(documentAuthorStyleSheets()); - collectActiveCSSStyleSheetsFromSeamlessParents(activeCSSStyleSheets, m_document); - filterEnabledNonemptyCSSStyleSheets(activeCSSStyleSheets, activeStyleSheets); - - StyleResolverUpdateType styleResolverUpdateType; - bool requiresFullStyleRecalc; - analyzeStyleSheetChange(updateFlag, activeCSSStyleSheets, styleResolverUpdateType, requiresFullStyleRecalc); - - if (styleResolverUpdateType == Reconstruct) - m_document.clearStyleResolver(); - else { - StyleResolver& styleResolver = m_document.ensureStyleResolver(); - if (styleResolverUpdateType == Reset) { - styleResolver.ruleSets().resetAuthorStyle(); - styleResolver.appendAuthorStyleSheets(0, activeCSSStyleSheets); - } else { - ASSERT(styleResolverUpdateType == Additive); - styleResolver.appendAuthorStyleSheets(m_activeAuthorStyleSheets.size(), activeCSSStyleSheets); - } - resetCSSFeatureFlags(); - } - - m_weakCopyOfActiveStyleSheetListForFastLookup.clear(); - m_activeAuthorStyleSheets.swap(activeCSSStyleSheets); - m_styleSheetsForStyleSheetList.swap(activeStyleSheets); - - m_usesRemUnits = styleSheetsUseRemUnits(m_activeAuthorStyleSheets); - m_pendingUpdateType = NoUpdate; - - m_document.notifySeamlessChildDocumentsOfStylesheetUpdate(); - - return requiresFullStyleRecalc; -} - -bool DocumentStyleSheetCollection::activeStyleSheetsContains(const CSSStyleSheet* sheet) const -{ - if (!m_weakCopyOfActiveStyleSheetListForFastLookup) { - m_weakCopyOfActiveStyleSheetListForFastLookup = adoptPtr(new HashSet<const CSSStyleSheet*>); - for (unsigned i = 0; i < m_activeAuthorStyleSheets.size(); ++i) - m_weakCopyOfActiveStyleSheetListForFastLookup->add(m_activeAuthorStyleSheets[i].get()); - } - return m_weakCopyOfActiveStyleSheetListForFastLookup->contains(sheet); -} - -void DocumentStyleSheetCollection::detachFromDocument() -{ - if (m_pageUserSheet) - m_pageUserSheet->detachFromDocument(); - for (unsigned i = 0; i < m_injectedUserStyleSheets.size(); ++i) - m_injectedUserStyleSheets[i]->detachFromDocument(); - for (unsigned i = 0; i < m_injectedAuthorStyleSheets.size(); ++i) - m_injectedAuthorStyleSheets[i]->detachFromDocument(); - for (unsigned i = 0; i < m_userStyleSheets.size(); ++i) - m_userStyleSheets[i]->detachFromDocument(); - for (unsigned i = 0; i < m_authorStyleSheets.size(); ++i) - m_authorStyleSheets[i]->detachFromDocument(); -} - -} diff --git a/Source/WebCore/dom/DocumentStyleSheetCollection.h b/Source/WebCore/dom/DocumentStyleSheetCollection.h deleted file mode 100644 index dcec209bb..000000000 --- a/Source/WebCore/dom/DocumentStyleSheetCollection.h +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (C) 1999 Lars Knoll (knoll@kde.org) - * (C) 1999 Antti Koivisto (koivisto@kde.org) - * (C) 2001 Dirk Mueller (mueller@kde.org) - * (C) 2006 Alexey Proskuryakov (ap@webkit.org) - * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012, 2013 Apple Inc. All rights reserved. - * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) - * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef DocumentStyleSheetCollection_h -#define DocumentStyleSheetCollection_h - -#include <wtf/FastMalloc.h> -#include <wtf/ListHashSet.h> -#include <wtf/RefPtr.h> -#include <wtf/Vector.h> -#include <wtf/text/WTFString.h> - -namespace WebCore { - -class CSSStyleSheet; -class Document; -class Node; -class StyleSheet; -class StyleSheetContents; -class StyleSheetList; - -class DocumentStyleSheetCollection { - WTF_MAKE_FAST_ALLOCATED; -public: - explicit DocumentStyleSheetCollection(Document&); - - const Vector<RefPtr<StyleSheet>>& styleSheetsForStyleSheetList() const { return m_styleSheetsForStyleSheetList; } - - const Vector<RefPtr<CSSStyleSheet>>& activeAuthorStyleSheets() const { return m_activeAuthorStyleSheets; } - - CSSStyleSheet* pageUserSheet(); - const Vector<RefPtr<CSSStyleSheet>>& documentUserStyleSheets() const { return m_userStyleSheets; } - const Vector<RefPtr<CSSStyleSheet>>& documentAuthorStyleSheets() const { return m_authorStyleSheets; } - const Vector<RefPtr<CSSStyleSheet>>& injectedUserStyleSheets() const; - const Vector<RefPtr<CSSStyleSheet>>& injectedAuthorStyleSheets() const; - - void addStyleSheetCandidateNode(Node&, bool createdByParser); - void removeStyleSheetCandidateNode(Node&); - - void clearPageUserSheet(); - void updatePageUserSheet(); - void invalidateInjectedStyleSheetCache(); - void updateInjectedStyleSheetCache() const; - - void addAuthorSheet(PassRef<StyleSheetContents> authorSheet); - void addUserSheet(PassRef<StyleSheetContents> userSheet); - - enum UpdateFlag { NoUpdate = 0, OptimizedUpdate, FullUpdate }; - - UpdateFlag pendingUpdateType() const { return m_pendingUpdateType; } - void setPendingUpdateType(UpdateFlag updateType) - { - if (updateType > m_pendingUpdateType) - m_pendingUpdateType = updateType; - } - - void flushPendingUpdates() - { - if (m_pendingUpdateType != NoUpdate) - updateActiveStyleSheets(m_pendingUpdateType); - } - - bool updateActiveStyleSheets(UpdateFlag); - - String preferredStylesheetSetName() const { return m_preferredStylesheetSetName; } - String selectedStylesheetSetName() const { return m_selectedStylesheetSetName; } - void setPreferredStylesheetSetName(const String& name) { m_preferredStylesheetSetName = name; } - void setSelectedStylesheetSetName(const String& name) { m_selectedStylesheetSetName = name; } - - void addPendingSheet() { m_pendingStylesheets++; } - enum RemovePendingSheetNotificationType { - RemovePendingSheetNotifyImmediately, - RemovePendingSheetNotifyLater - }; - void removePendingSheet(RemovePendingSheetNotificationType = RemovePendingSheetNotifyImmediately); - - bool hasPendingSheets() const { return m_pendingStylesheets > 0; } - - bool usesSiblingRules() const { return m_usesSiblingRules || m_usesSiblingRulesOverride; } - void setUsesSiblingRulesOverride(bool b) { m_usesSiblingRulesOverride = b; } - bool usesFirstLineRules() const { return m_usesFirstLineRules; } - bool usesFirstLetterRules() const { return m_usesFirstLetterRules; } - void setUsesFirstLetterRules(bool b) { m_usesFirstLetterRules = b; } - bool usesBeforeAfterRules() const { return m_usesBeforeAfterRules || m_usesBeforeAfterRulesOverride; } - void setUsesBeforeAfterRulesOverride(bool b) { m_usesBeforeAfterRulesOverride = b; } - bool usesRemUnits() const { return m_usesRemUnits; } - void setUsesRemUnit(bool b) { m_usesRemUnits = b; } - - void combineCSSFeatureFlags(); - void resetCSSFeatureFlags(); - - bool activeStyleSheetsContains(const CSSStyleSheet*) const; - - void detachFromDocument(); - -private: - void collectActiveStyleSheets(Vector<RefPtr<StyleSheet>>&); - enum StyleResolverUpdateType { - Reconstruct, - Reset, - Additive - }; - void analyzeStyleSheetChange(UpdateFlag, const Vector<RefPtr<CSSStyleSheet>>& newStylesheets, StyleResolverUpdateType&, bool& requiresFullStyleRecalc); - - Document& m_document; - - Vector<RefPtr<StyleSheet>> m_styleSheetsForStyleSheetList; - Vector<RefPtr<CSSStyleSheet>> m_activeAuthorStyleSheets; - - // This is a mirror of m_activeAuthorStyleSheets that gets populated on demand for activeStyleSheetsContains(). - mutable OwnPtr<HashSet<const CSSStyleSheet*>> m_weakCopyOfActiveStyleSheetListForFastLookup; - - // Track the number of currently loading top-level stylesheets needed for rendering. - // Sheets loaded using the @import directive are not included in this count. - // We use this count of pending sheets to detect when we can begin attaching - // elements and when it is safe to execute scripts. - int m_pendingStylesheets; - - RefPtr<CSSStyleSheet> m_pageUserSheet; - - mutable Vector<RefPtr<CSSStyleSheet>> m_injectedUserStyleSheets; - mutable Vector<RefPtr<CSSStyleSheet>> m_injectedAuthorStyleSheets; - mutable bool m_injectedStyleSheetCacheValid; - - Vector<RefPtr<CSSStyleSheet>> m_userStyleSheets; - Vector<RefPtr<CSSStyleSheet>> m_authorStyleSheets; - - bool m_hadActiveLoadingStylesheet; - UpdateFlag m_pendingUpdateType; - - typedef ListHashSet<Node*, 32> StyleSheetCandidateListHashSet; - StyleSheetCandidateListHashSet m_styleSheetCandidateNodes; - - String m_preferredStylesheetSetName; - String m_selectedStylesheetSetName; - - bool m_usesSiblingRules; - bool m_usesSiblingRulesOverride; - bool m_usesFirstLineRules; - bool m_usesFirstLetterRules; - bool m_usesBeforeAfterRules; - bool m_usesBeforeAfterRulesOverride; - bool m_usesRemUnits; -}; - -} - -#endif - diff --git a/Source/WebCore/dom/DocumentTiming.h b/Source/WebCore/dom/DocumentTiming.h index 8fa8dca38..bbf029c21 100644 --- a/Source/WebCore/dom/DocumentTiming.h +++ b/Source/WebCore/dom/DocumentTiming.h @@ -23,32 +23,22 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DocumentTiming_h -#define DocumentTiming_h +#pragma once #if ENABLE(WEB_TIMING) +#include <wtf/MonotonicTime.h> + namespace WebCore { struct DocumentTiming { - DocumentTiming() - : domLoading(0.0) - , domInteractive(0.0) - , domContentLoadedEventStart(0.0) - , domContentLoadedEventEnd(0.0) - , domComplete(0.0) - { - } - - double domLoading; - double domInteractive; - double domContentLoadedEventStart; - double domContentLoadedEventEnd; - double domComplete; + MonotonicTime domLoading; + MonotonicTime domInteractive; + MonotonicTime domContentLoadedEventStart; + MonotonicTime domContentLoadedEventEnd; + MonotonicTime domComplete; }; -} - -#endif +} // namespace WebCore -#endif +#endif // ENABLE(WEB_TIMING) diff --git a/Source/WebCore/dom/DocumentType.cpp b/Source/WebCore/dom/DocumentType.cpp index 472f84e0c..1394ecfd5 100644 --- a/Source/WebCore/dom/DocumentType.cpp +++ b/Source/WebCore/dom/DocumentType.cpp @@ -29,18 +29,13 @@ namespace WebCore { DocumentType::DocumentType(Document& document, const String& name, const String& publicId, const String& systemId) - : Node(&document, CreateOther) + : Node(document, CreateOther) , m_name(name) - , m_publicId(publicId) - , m_systemId(systemId) + , m_publicId(publicId.isNull() ? emptyString() : publicId) + , m_systemId(systemId.isNull() ? emptyString() : systemId) { } -URL DocumentType::baseURI() const -{ - return URL(); -} - String DocumentType::nodeName() const { return name(); @@ -51,9 +46,9 @@ Node::NodeType DocumentType::nodeType() const return DOCUMENT_TYPE_NODE; } -PassRefPtr<Node> DocumentType::cloneNode(bool /*deep*/) +Ref<Node> DocumentType::cloneNodeInternal(Document& documentTarget, CloningOperation) { - return create(document(), m_name, m_publicId, m_systemId); + return create(documentTarget, m_name, m_publicId, m_systemId); } } diff --git a/Source/WebCore/dom/DocumentType.h b/Source/WebCore/dom/DocumentType.h index df8578b8b..64928376f 100644 --- a/Source/WebCore/dom/DocumentType.h +++ b/Source/WebCore/dom/DocumentType.h @@ -21,8 +21,7 @@ * */ -#ifndef DocumentType_h -#define DocumentType_h +#pragma once #include "Node.h" @@ -32,44 +31,29 @@ class NamedNodeMap; class DocumentType final : public Node { public: - static PassRefPtr<DocumentType> create(Document& document, const String& name, const String& publicId, const String& systemId) + static Ref<DocumentType> create(Document& document, const String& name, const String& publicId, const String& systemId) { - return adoptRef(new DocumentType(document, name, publicId, systemId)); + return adoptRef(*new DocumentType(document, name, publicId, systemId)); } - // FIXME: We never fill m_entities and m_notations. Current implementation of NamedNodeMap doesn't work without an associated Element yet. - NamedNodeMap* entities() const { return m_entities.get(); } - NamedNodeMap* notations() const { return m_notations.get(); } - const String& name() const { return m_name; } const String& publicId() const { return m_publicId; } const String& systemId() const { return m_systemId; } - const String& internalSubset() const { return m_subset; } private: DocumentType(Document&, const String& name, const String& publicId, const String& systemId); - virtual URL baseURI() const override; - virtual String nodeName() const override; - virtual NodeType nodeType() const override; - virtual PassRefPtr<Node> cloneNode(bool deep) override; - - OwnPtr<NamedNodeMap> m_entities; - OwnPtr<NamedNodeMap> m_notations; + String nodeName() const override; + NodeType nodeType() const override; + Ref<Node> cloneNodeInternal(Document&, CloningOperation) override; String m_name; String m_publicId; String m_systemId; - String m_subset; }; -inline bool isDocumentType(const Node& node) -{ - return node.nodeType() == Node::DOCUMENT_TYPE_NODE; -} - -NODE_TYPE_CASTS(DocumentType) - } // namespace WebCore -#endif +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::DocumentType) + static bool isType(const WebCore::Node& node) { return node.nodeType() == WebCore::Node::DOCUMENT_TYPE_NODE; } +SPECIALIZE_TYPE_TRAITS_END() diff --git a/Source/WebCore/dom/DocumentType.idl b/Source/WebCore/dom/DocumentType.idl index a6ed639b5..e1ea5eb2f 100644 --- a/Source/WebCore/dom/DocumentType.idl +++ b/Source/WebCore/dom/DocumentType.idl @@ -19,20 +19,11 @@ [ JSGenerateToNativeObject, + JSGenerateToJSObject ] interface DocumentType : Node { - - // DOM Level 1 - readonly attribute DOMString name; - readonly attribute NamedNodeMap entities; - readonly attribute NamedNodeMap notations; - - // DOM Level 2 - - [TreatReturnedNullStringAs=Null] readonly attribute DOMString publicId; - [TreatReturnedNullStringAs=Null] readonly attribute DOMString systemId; - [TreatReturnedNullStringAs=Null] readonly attribute DOMString internalSubset; + readonly attribute DOMString publicId; + readonly attribute DOMString systemId; }; DocumentType implements ChildNode; - diff --git a/Source/WebCore/dom/Element.cpp b/Source/WebCore/dom/Element.cpp index 46ee2e83f..bd43d03eb 100644 --- a/Source/WebCore/dom/Element.cpp +++ b/Source/WebCore/dom/Element.cpp @@ -4,7 +4,7 @@ * (C) 2001 Peter Kelly (pmk@post.com) * (C) 2001 Dirk Mueller (mueller@kde.org) * (C) 2007 David Smith (catfish.man@gmail.com) - * Copyright (C) 2004-2014 Apple Inc. All rights reserved. + * Copyright (C) 2004-2017 Apple Inc. All rights reserved. * (C) 2007 Eric Seidel (eric@webkit.org) * * This library is free software; you can redistribute it and/or @@ -28,124 +28,152 @@ #include "AXObjectCache.h" #include "Attr.h" +#include "AttributeChangeInvalidation.h" #include "CSSParser.h" #include "Chrome.h" #include "ChromeClient.h" +#include "ClassChangeInvalidation.h" #include "ClientRect.h" #include "ClientRectList.h" +#include "ComposedTreeAncestorIterator.h" #include "ContainerNodeAlgorithms.h" +#include "CustomElementReactionQueue.h" +#include "CustomElementRegistry.h" #include "DOMTokenList.h" +#include "DocumentAnimation.h" #include "DocumentSharedObjectPool.h" #include "ElementIterator.h" #include "ElementRareData.h" #include "EventDispatcher.h" +#include "EventHandler.h" +#include "EventNames.h" #include "FlowThreadController.h" #include "FocusController.h" #include "FocusEvent.h" #include "FrameSelection.h" #include "FrameView.h" +#include "HTMLBodyElement.h" +#include "HTMLCanvasElement.h" #include "HTMLCollection.h" #include "HTMLDocument.h" -#include "HTMLElement.h" -#include "HTMLFormControlsCollection.h" +#include "HTMLHtmlElement.h" #include "HTMLLabelElement.h" #include "HTMLNameCollection.h" -#include "HTMLOptionsCollection.h" +#include "HTMLObjectElement.h" #include "HTMLParserIdioms.h" -#include "HTMLSelectElement.h" -#include "HTMLTableRowsCollection.h" +#include "HTMLTemplateElement.h" +#include "IdChangeInvalidation.h" #include "IdTargetObserverRegistry.h" -#include "InsertionPoint.h" +#include "InspectorInstrumentation.h" +#include "JSLazyEventListener.h" #include "KeyboardEvent.h" +#include "KeyframeEffect.h" +#include "MainFrame.h" #include "MutationObserverInterestGroup.h" #include "MutationRecord.h" +#include "NoEventDispatchAssertion.h" #include "NodeRenderStyle.h" #include "PlatformWheelEvent.h" #include "PointerLockController.h" +#include "RenderFlowThread.h" +#include "RenderLayer.h" #include "RenderNamedFlowFragment.h" #include "RenderRegion.h" #include "RenderTheme.h" +#include "RenderTreeUpdater.h" #include "RenderView.h" #include "RenderWidget.h" +#include "SVGDocumentExtensions.h" +#include "SVGElement.h" +#include "SVGNames.h" +#include "SVGSVGElement.h" +#include "ScrollLatchingState.h" #include "SelectorQuery.h" #include "Settings.h" +#include "SimulatedClick.h" +#include "SlotAssignment.h" #include "StyleProperties.h" #include "StyleResolver.h" +#include "StyleScope.h" +#include "StyleTreeResolver.h" #include "TextIterator.h" #include "VoidCallback.h" #include "WheelEvent.h" +#include "XLinkNames.h" #include "XMLNSNames.h" #include "XMLNames.h" #include "htmlediting.h" -#include <wtf/BitVector.h> +#include "markup.h" #include <wtf/CurrentTime.h> +#include <wtf/NeverDestroyed.h> #include <wtf/text/CString.h> -#if ENABLE(SVG) -#include "SVGDocumentExtensions.h" -#include "SVGElement.h" -#include "SVGNames.h" -#endif - namespace WebCore { using namespace HTMLNames; using namespace XMLNames; -static inline bool shouldIgnoreAttributeCase(const Element& element) +static HashMap<Element*, Vector<RefPtr<Attr>>>& attrNodeListMap() { - return element.isHTMLElement() && element.document().isHTMLDocument(); + static NeverDestroyed<HashMap<Element*, Vector<RefPtr<Attr>>>> map; + return map; } -typedef Vector<RefPtr<Attr>> AttrNodeList; -typedef HashMap<Element*, OwnPtr<AttrNodeList>> AttrNodeListMap; +static Vector<RefPtr<Attr>>* attrNodeListForElement(Element& element) +{ + if (!element.hasSyntheticAttrChildNodes()) + return nullptr; + ASSERT(attrNodeListMap().contains(&element)); + return &attrNodeListMap().find(&element)->value; +} -static AttrNodeListMap& attrNodeListMap() +static Vector<RefPtr<Attr>>& ensureAttrNodeListForElement(Element& element) { - DEFINE_STATIC_LOCAL(AttrNodeListMap, map, ()); - return map; + if (element.hasSyntheticAttrChildNodes()) { + ASSERT(attrNodeListMap().contains(&element)); + return attrNodeListMap().find(&element)->value; + } + ASSERT(!attrNodeListMap().contains(&element)); + element.setHasSyntheticAttrChildNodes(true); + return attrNodeListMap().add(&element, Vector<RefPtr<Attr>>()).iterator->value; } -static AttrNodeList* attrNodeListForElement(Element* element) +static void removeAttrNodeListForElement(Element& element) { - if (!element->hasSyntheticAttrChildNodes()) - return 0; - ASSERT(attrNodeListMap().contains(element)); - return attrNodeListMap().get(element); + ASSERT(element.hasSyntheticAttrChildNodes()); + ASSERT(attrNodeListMap().contains(&element)); + attrNodeListMap().remove(&element); + element.setHasSyntheticAttrChildNodes(false); } -static AttrNodeList& ensureAttrNodeListForElement(Element* element) +static Attr* findAttrNodeInList(Vector<RefPtr<Attr>>& attrNodeList, const QualifiedName& name) { - if (element->hasSyntheticAttrChildNodes()) { - ASSERT(attrNodeListMap().contains(element)); - return *attrNodeListMap().get(element); + for (auto& node : attrNodeList) { + if (node->qualifiedName().matches(name)) + return node.get(); } - ASSERT(!attrNodeListMap().contains(element)); - element->setHasSyntheticAttrChildNodes(true); - AttrNodeListMap::AddResult result = attrNodeListMap().add(element, adoptPtr(new AttrNodeList)); - return *result.iterator->value; + return nullptr; } -static void removeAttrNodeListForElement(Element* element) +static Attr* findAttrNodeInList(Vector<RefPtr<Attr>>& attrNodeList, const AtomicString& localName, bool shouldIgnoreAttributeCase) { - ASSERT(element->hasSyntheticAttrChildNodes()); - ASSERT(attrNodeListMap().contains(element)); - attrNodeListMap().remove(element); - element->setHasSyntheticAttrChildNodes(false); + const AtomicString& caseAdjustedName = shouldIgnoreAttributeCase ? localName.convertToASCIILowercase() : localName; + for (auto& node : attrNodeList) { + if (node->qualifiedName().localName() == caseAdjustedName) + return node.get(); + } + return nullptr; } -static Attr* findAttrNodeInList(AttrNodeList& attrNodeList, const QualifiedName& name) +Ref<Element> Element::create(const QualifiedName& tagName, Document& document) { - for (unsigned i = 0; i < attrNodeList.size(); ++i) { - if (attrNodeList.at(i)->qualifiedName() == name) - return attrNodeList.at(i).get(); - } - return 0; + return adoptRef(*new Element(tagName, document, CreateElement)); } -PassRefPtr<Element> Element::create(const QualifiedName& tagName, Document& document) +Element::Element(const QualifiedName& tagName, Document& document, ConstructionType type) + : ContainerNode(document, type) + , m_tagName(tagName) { - return adoptRef(new Element(tagName, document, CreateElement)); } Element::~Element() @@ -154,8 +182,8 @@ Element::~Element() if (document().hasLivingRenderTree()) { // When the document is not destroyed, an element that was part of a named flow // content nodes should have been removed from the content nodes collection - // and the inNamedFlow flag reset. - ASSERT(!inNamedFlow()); + // and the isNamedFlowContentElement flag reset. + ASSERT_WITH_SECURITY_IMPLICATION(!isNamedFlowContentElement()); } #endif @@ -167,12 +195,10 @@ Element::~Element() if (hasSyntheticAttrChildNodes()) detachAllAttrNodesFromElement(); -#if ENABLE(SVG) if (hasPendingResources()) { - document().accessSVGExtensions()->removeElementFromPendingResources(this); + document().accessSVGExtensions().removeElementFromPendingResources(this); ASSERT(!hasPendingResources()); } -#endif } inline ElementRareData* Element::elementRareData() const @@ -192,27 +218,37 @@ void Element::clearTabIndexExplicitlyIfNeeded() elementRareData()->clearTabIndexExplicitly(); } -void Element::setTabIndexExplicitly(short tabIndex) +void Element::setTabIndexExplicitly(int tabIndex) { ensureElementRareData().setTabIndexExplicitly(tabIndex); } -bool Element::supportsFocus() const +bool Element::tabIndexSetExplicitly() const { return hasRareData() && elementRareData()->tabIndexSetExplicitly(); } +bool Element::supportsFocus() const +{ + return tabIndexSetExplicitly(); +} + Element* Element::focusDelegate() { return this; } -short Element::tabIndex() const +int Element::tabIndex() const { return hasRareData() ? elementRareData()->tabIndex() : 0; } -bool Element::isKeyboardFocusable(KeyboardEvent*) const +void Element::setTabIndex(int value) +{ + setIntegralAttribute(tabindexAttr, value); +} + +bool Element::isKeyboardFocusable(KeyboardEvent&) const { return isFocusable() && tabIndex() >= 0; } @@ -224,7 +260,12 @@ bool Element::isMouseFocusable() const bool Element::shouldUseInputMethod() { - return isContentEditable(UserSelectAllIsAlwaysNonEditable); + return computeEditability(UserSelectAllIsAlwaysNonEditable, ShouldUpdateStyle::Update) != Editability::ReadOnly; +} + +static bool isForceEvent(const PlatformMouseEvent& platformEvent) +{ + return platformEvent.type() == PlatformEvent::MouseForceChanged || platformEvent.type() == PlatformEvent::MouseForceDown || platformEvent.type() == PlatformEvent::MouseForceUp; } bool Element::dispatchMouseEvent(const PlatformMouseEvent& platformEvent, const AtomicString& eventType, int detail, Element* relatedTarget) @@ -232,7 +273,10 @@ bool Element::dispatchMouseEvent(const PlatformMouseEvent& platformEvent, const if (isDisabledFormControl()) return false; - RefPtr<MouseEvent> mouseEvent = MouseEvent::create(eventType, document().defaultView(), platformEvent, detail, relatedTarget); + if (isForceEvent(platformEvent) && !document().hasListenerTypeForEventType(platformEvent.type())) + return false; + + Ref<MouseEvent> mouseEvent = MouseEvent::create(eventType, document().defaultView(), platformEvent, detail, relatedTarget); if (mouseEvent->type().isEmpty()) return true; // Shouldn't happen. @@ -244,12 +288,11 @@ bool Element::dispatchMouseEvent(const PlatformMouseEvent& platformEvent, const // Special case: If it's a double click event, we also send the dblclick event. This is not part // of the DOM specs, but is used for compatibility with the ondblclick="" attribute. This is treated // as a separate event in other DOM-compliant browsers like Firefox, and so we do the same. - RefPtr<MouseEvent> doubleClickEvent = MouseEvent::create(); - doubleClickEvent->initMouseEvent(eventNames().dblclickEvent, + Ref<MouseEvent> doubleClickEvent = MouseEvent::create(eventNames().dblclickEvent, mouseEvent->bubbles(), mouseEvent->cancelable(), mouseEvent->view(), mouseEvent->detail(), mouseEvent->screenX(), mouseEvent->screenY(), mouseEvent->clientX(), mouseEvent->clientY(), mouseEvent->ctrlKey(), mouseEvent->altKey(), mouseEvent->shiftKey(), mouseEvent->metaKey(), - mouseEvent->button(), relatedTarget); + mouseEvent->button(), mouseEvent->syntheticClickType(), relatedTarget); if (mouseEvent->defaultHandled()) doubleClickEvent->setDefaultHandled(); @@ -261,75 +304,73 @@ bool Element::dispatchMouseEvent(const PlatformMouseEvent& platformEvent, const return didNotSwallowEvent; } -inline static unsigned deltaMode(const PlatformWheelEvent& event) -{ - return event.granularity() == ScrollByPageWheelEvent ? WheelEvent::DOM_DELTA_PAGE : WheelEvent::DOM_DELTA_PIXEL; -} bool Element::dispatchWheelEvent(const PlatformWheelEvent& event) { - if (!(event.deltaX() || event.deltaY())) - return true; + Ref<WheelEvent> wheelEvent = WheelEvent::create(event, document().defaultView()); - RefPtr<WheelEvent> wheelEvent = WheelEvent::create( - FloatPoint(event.wheelTicksX(), event.wheelTicksY()), - FloatPoint(event.deltaX(), event.deltaY()), - deltaMode(event), - document().defaultView(), - event.globalPosition(), - event.position(), - event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey(), - event.directionInvertedFromDevice(), - event.timestamp()); + // Events with no deltas are important because they convey platform information about scroll gestures + // and momentum beginning or ending. However, those events should not be sent to the DOM since some + // websites will break. They need to be dispatched because dispatching them will call into the default + // event handler, and our platform code will correctly handle the phase changes. Calling stopPropogation() + // will prevent the event from being sent to the DOM, but will still call the default event handler. + if (!event.deltaX() && !event.deltaY()) + wheelEvent->stopPropagation(); - return EventDispatcher::dispatchEvent(this, wheelEvent) && !wheelEvent->defaultHandled(); + return EventDispatcher::dispatchEvent(*this, wheelEvent) && !wheelEvent->defaultHandled(); } bool Element::dispatchKeyEvent(const PlatformKeyboardEvent& platformEvent) { - RefPtr<KeyboardEvent> event = KeyboardEvent::create(platformEvent, document().defaultView()); - return EventDispatcher::dispatchEvent(this, event) && !event->defaultHandled(); + Ref<KeyboardEvent> event = KeyboardEvent::create(platformEvent, document().defaultView()); + if (Frame* frame = document().frame()) { + if (frame->eventHandler().accessibilityPreventsEventPropogation(event)) + event->stopPropagation(); + } + return EventDispatcher::dispatchEvent(*this, event) && !event->defaultHandled(); } void Element::dispatchSimulatedClick(Event* underlyingEvent, SimulatedClickMouseEventOptions eventOptions, SimulatedClickVisualOptions visualOptions) { - EventDispatcher::dispatchSimulatedClick(this, underlyingEvent, eventOptions, visualOptions); + simulateClick(*this, underlyingEvent, eventOptions, visualOptions, SimulatedClickSource::UserAgent); } -DEFINE_VIRTUAL_ATTRIBUTE_EVENT_LISTENER(Element, blur); -DEFINE_VIRTUAL_ATTRIBUTE_EVENT_LISTENER(Element, error); -DEFINE_VIRTUAL_ATTRIBUTE_EVENT_LISTENER(Element, focus); -DEFINE_VIRTUAL_ATTRIBUTE_EVENT_LISTENER(Element, load); - -PassRefPtr<Node> Element::cloneNode(bool deep) +Ref<Node> Element::cloneNodeInternal(Document& targetDocument, CloningOperation type) { - return deep ? cloneElementWithChildren() : cloneElementWithoutChildren(); + switch (type) { + case CloningOperation::OnlySelf: + case CloningOperation::SelfWithTemplateContent: + return cloneElementWithoutChildren(targetDocument); + case CloningOperation::Everything: + break; + } + return cloneElementWithChildren(targetDocument); } -PassRefPtr<Element> Element::cloneElementWithChildren() +Ref<Element> Element::cloneElementWithChildren(Document& targetDocument) { - RefPtr<Element> clone = cloneElementWithoutChildren(); - cloneChildNodes(clone.get()); - return clone.release(); + Ref<Element> clone = cloneElementWithoutChildren(targetDocument); + cloneChildNodes(clone); + return clone; } -PassRefPtr<Element> Element::cloneElementWithoutChildren() +Ref<Element> Element::cloneElementWithoutChildren(Document& targetDocument) { - RefPtr<Element> clone = cloneElementWithoutAttributesAndChildren(); + Ref<Element> clone = cloneElementWithoutAttributesAndChildren(targetDocument); // This will catch HTML elements in the wrong namespace that are not correctly copied. // This is a sanity check as HTML overloads some of the DOM methods. ASSERT(isHTMLElement() == clone->isHTMLElement()); clone->cloneDataFromElement(*this); - return clone.release(); + return clone; } -PassRefPtr<Element> Element::cloneElementWithoutAttributesAndChildren() +Ref<Element> Element::cloneElementWithoutAttributesAndChildren(Document& targetDocument) { - return document().createElement(tagQName(), false); + return targetDocument.createElement(tagQName(), false); } -PassRefPtr<Attr> Element::detachAttribute(unsigned index) +Ref<Attr> Element::detachAttribute(unsigned index) { ASSERT(elementData()); @@ -342,19 +383,20 @@ PassRefPtr<Attr> Element::detachAttribute(unsigned index) attrNode = Attr::create(document(), attribute.name(), attribute.value()); removeAttributeInternal(index, NotInSynchronizationOfLazyAttribute); - return attrNode.release(); + return attrNode.releaseNonNull(); } -void Element::removeAttribute(const QualifiedName& name) +bool Element::removeAttribute(const QualifiedName& name) { if (!elementData()) - return; + return false; unsigned index = elementData()->findAttributeIndexByName(name); if (index == ElementData::attributeNotFound) - return; + return false; removeAttributeInternal(index, NotInSynchronizationOfLazyAttribute); + return true; } void Element::setBooleanAttribute(const QualifiedName& name, bool value) @@ -365,14 +407,14 @@ void Element::setBooleanAttribute(const QualifiedName& name, bool value) removeAttribute(name); } -NamedNodeMap* Element::attributes() const +NamedNodeMap& Element::attributes() const { ElementRareData& rareData = const_cast<Element*>(this)->ensureElementRareData(); if (NamedNodeMap* attributeMap = rareData.attributeMap()) - return attributeMap; + return *attributeMap; - rareData.setAttributeMap(NamedNodeMap::create(const_cast<Element&>(*this))); - return rareData.attributeMap(); + rareData.setAttributeMap(std::make_unique<NamedNodeMap>(const_cast<Element&>(*this))); + return *rareData.attributeMap(); } Node::NodeType Element::nodeType() const @@ -393,15 +435,14 @@ void Element::synchronizeAllAttributes() const ASSERT(isStyledElement()); static_cast<const StyledElement*>(this)->synchronizeStyleAttributeInternal(); } -#if ENABLE(SVG) + if (elementData()->animatedSVGAttributesAreDirty()) { ASSERT(isSVGElement()); - toSVGElement(this)->synchronizeAnimatedSVGAttribute(anyQName()); + downcast<SVGElement>(*this).synchronizeAnimatedSVGAttribute(anyQName()); } -#endif } -inline void Element::synchronizeAttribute(const QualifiedName& name) const +ALWAYS_INLINE void Element::synchronizeAttribute(const QualifiedName& name) const { if (!elementData()) return; @@ -410,32 +451,36 @@ inline void Element::synchronizeAttribute(const QualifiedName& name) const static_cast<const StyledElement*>(this)->synchronizeStyleAttributeInternal(); return; } -#if ENABLE(SVG) + if (UNLIKELY(elementData()->animatedSVGAttributesAreDirty())) { ASSERT(isSVGElement()); - toSVGElement(this)->synchronizeAnimatedSVGAttribute(name); + downcast<SVGElement>(*this).synchronizeAnimatedSVGAttribute(name); } -#endif } -inline void Element::synchronizeAttribute(const AtomicString& localName) const +static ALWAYS_INLINE bool isStyleAttribute(const Element& element, const AtomicString& attributeLocalName) +{ + if (shouldIgnoreAttributeCase(element)) + return equalLettersIgnoringASCIICase(attributeLocalName, "style"); + return attributeLocalName == styleAttr.localName(); +} + +ALWAYS_INLINE void Element::synchronizeAttribute(const AtomicString& localName) const { // This version of synchronizeAttribute() is streamlined for the case where you don't have a full QualifiedName, // e.g when called from DOM API. if (!elementData()) return; - if (elementData()->styleAttributeIsDirty() && equalPossiblyIgnoringCase(localName, styleAttr.localName(), shouldIgnoreAttributeCase(*this))) { + if (elementData()->styleAttributeIsDirty() && isStyleAttribute(*this, localName)) { ASSERT_WITH_SECURITY_IMPLICATION(isStyledElement()); static_cast<const StyledElement*>(this)->synchronizeStyleAttributeInternal(); return; } -#if ENABLE(SVG) if (elementData()->animatedSVGAttributesAreDirty()) { // We're not passing a namespace argument on purpose. SVGNames::*Attr are defined w/o namespaces as well. ASSERT_WITH_SECURITY_IMPLICATION(isSVGElement()); - toSVGElement(this)->synchronizeAnimatedSVGAttribute(QualifiedName(nullAtom, localName, nullAtom)); + downcast<SVGElement>(*this).synchronizeAnimatedSVGAttribute(QualifiedName(nullAtom, localName, nullAtom)); } -#endif } const AtomicString& Element::getAttribute(const QualifiedName& name) const @@ -448,25 +493,33 @@ const AtomicString& Element::getAttribute(const QualifiedName& name) const return nullAtom; } +Vector<String> Element::getAttributeNames() const +{ + Vector<String> attributesVector; + if (!hasAttributes()) + return attributesVector; + + auto attributes = attributesIterator(); + attributesVector.reserveInitialCapacity(attributes.attributeCount()); + for (auto& attribute : attributes) + attributesVector.uncheckedAppend(attribute.name().toString()); + return attributesVector; +} + bool Element::isFocusable() const { - if (!inDocument() || !supportsFocus()) + if (!isConnected() || !supportsFocus()) return false; - // Elements in canvas fallback content are not rendered, but they are allowed to be - // focusable as long as their canvas is displayed and visible. - if (isInCanvasSubtree()) { - const Element* e = this; - while (e && !e->hasLocalName(canvasTag)) - e = e->parentElement(); - ASSERT(e); - return e->renderer() && e->renderer()->style().visibility() == VISIBLE; - } - if (!renderer()) { // If the node is in a display:none tree it might say it needs style recalc but // the whole document is actually up to date. ASSERT(!needsStyleRecalc() || !document().childNeedsStyleRecalc()); + + // Elements in canvas fallback content are not rendered, but they are allowed to be + // focusable as long as their canvas is displayed and visible. + if (auto* canvas = ancestorsOfType<HTMLCanvasElement>(*this).first()) + return canvas->renderer() && canvas->renderer()->style().visibility() == VISIBLE; } // FIXME: Even if we are not visible, we might have a child that is visible. @@ -508,14 +561,15 @@ void Element::setActive(bool flag, bool pause) document().userActionElements().setActive(this, flag); + const RenderStyle* renderStyle = this->renderStyle(); + bool reactsToPress = (renderStyle && renderStyle->affectedByActive()) || styleAffectedByActive(); + if (reactsToPress) + invalidateStyleForSubtree(); + if (!renderer()) return; - bool reactsToPress = renderStyle()->affectedByActive() || childrenAffectedByActive(); - if (reactsToPress) - setNeedsStyleRecalc(); - - if (renderer()->style().hasAppearance() && renderer()->theme().stateChanged(renderer(), PressedState)) + if (renderer()->style().hasAppearance() && renderer()->theme().stateChanged(*renderer(), ControlStates::PressedState)) reactsToPress = true; // The rest of this function implements a feature that only works if the @@ -537,7 +591,7 @@ void Element::setActive(bool flag, bool pause) // Do an immediate repaint. if (renderer()) - renderer()->repaint(true); + renderer()->repaint(); // FIXME: Come up with a less ridiculous way of doing this. #ifdef HAVE_FUNC_USLEEP @@ -555,7 +609,10 @@ void Element::setFocus(bool flag) return; document().userActionElements().setFocused(this, flag); - setNeedsStyleRecalc(); + invalidateStyleForSubtree(); + + for (Element* element = this; element; element = element->parentOrShadowHostElement()) + element->setHasFocusWithin(flag); } void Element::setHovered(bool flag) @@ -572,16 +629,16 @@ void Element::setHovered(bool flag) // style, it would never go back to its normal style and remain // stuck in its hovered style). if (!flag) - setNeedsStyleRecalc(); + invalidateStyleForSubtree(); return; } if (renderer()->style().affectedByHover() || childrenAffectedByHover()) - setNeedsStyleRecalc(); + invalidateStyleForSubtree(); if (renderer()->style().hasAppearance()) - renderer()->theme().stateChanged(renderer(), HoverState); + renderer()->theme().stateChanged(*renderer(), ControlStates::HoverState); } void Element::scrollIntoView(bool alignToTop) @@ -591,12 +648,13 @@ void Element::scrollIntoView(bool alignToTop) if (!renderer()) return; - LayoutRect bounds = boundingBox(); + bool insideFixed; + LayoutRect absoluteBounds = renderer()->absoluteAnchorRect(&insideFixed); // Align to the top / bottom and to the closest edge. if (alignToTop) - renderer()->scrollRectToVisible(bounds, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignTopAlways); + renderer()->scrollRectToVisible(SelectionRevealMode::Reveal, absoluteBounds, insideFixed, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignTopAlways); else - renderer()->scrollRectToVisible(bounds, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignBottomAlways); + renderer()->scrollRectToVisible(SelectionRevealMode::Reveal, absoluteBounds, insideFixed, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignBottomAlways); } void Element::scrollIntoViewIfNeeded(bool centerIfNeeded) @@ -606,21 +664,81 @@ void Element::scrollIntoViewIfNeeded(bool centerIfNeeded) if (!renderer()) return; - LayoutRect bounds = boundingBox(); + bool insideFixed; + LayoutRect absoluteBounds = renderer()->absoluteAnchorRect(&insideFixed); if (centerIfNeeded) - renderer()->scrollRectToVisible(bounds, ScrollAlignment::alignCenterIfNeeded, ScrollAlignment::alignCenterIfNeeded); + renderer()->scrollRectToVisible(SelectionRevealMode::Reveal, absoluteBounds, insideFixed, ScrollAlignment::alignCenterIfNeeded, ScrollAlignment::alignCenterIfNeeded); else - renderer()->scrollRectToVisible(bounds, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignToEdgeIfNeeded); + renderer()->scrollRectToVisible(SelectionRevealMode::Reveal, absoluteBounds, insideFixed, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignToEdgeIfNeeded); +} + +void Element::scrollIntoViewIfNotVisible(bool centerIfNotVisible) +{ + document().updateLayoutIgnorePendingStylesheets(); + + if (!renderer()) + return; + + bool insideFixed; + LayoutRect absoluteBounds = renderer()->absoluteAnchorRect(&insideFixed); + if (centerIfNotVisible) + renderer()->scrollRectToVisible(SelectionRevealMode::Reveal, absoluteBounds, insideFixed, ScrollAlignment::alignCenterIfNotVisible, ScrollAlignment::alignCenterIfNotVisible); + else + renderer()->scrollRectToVisible(SelectionRevealMode::Reveal, absoluteBounds, insideFixed, ScrollAlignment::alignToEdgeIfNotVisible, ScrollAlignment::alignToEdgeIfNotVisible); +} + +void Element::scrollBy(const ScrollToOptions& options) +{ + return scrollBy(options.left.value_or(0), options.top.value_or(0)); +} + +static inline double normalizeNonFiniteValue(double f) +{ + return std::isfinite(f) ? f : 0; +} + +void Element::scrollBy(double x, double y) +{ + scrollTo(scrollLeft() + normalizeNonFiniteValue(x), scrollTop() + normalizeNonFiniteValue(y)); +} + +void Element::scrollTo(const ScrollToOptions& options) +{ + // If the element is the root element and document is in quirks mode, terminate these steps. + // Note that WebKit always uses quirks mode document scrolling behavior. See Document::scrollingElement(). + if (this == document().documentElement()) + return; + + document().updateLayoutIgnorePendingStylesheets(); + + // If the element does not have any associated CSS layout box, the element has no associated scrolling box, + // or the element has no overflow, terminate these steps. + RenderBox* renderer = renderBox(); + if (!renderer || !renderer->hasOverflowClip()) + return; + + // Normalize non-finite values for left and top dictionary members of options, if present. + double x = options.left ? normalizeNonFiniteValue(options.left.value()) : adjustForAbsoluteZoom(renderer->scrollLeft(), *renderer); + double y = options.top ? normalizeNonFiniteValue(options.top.value()) : adjustForAbsoluteZoom(renderer->scrollTop(), *renderer); + + renderer->setScrollLeft(clampToInteger(x * renderer->style().effectiveZoom())); + renderer->setScrollTop(clampToInteger(y * renderer->style().effectiveZoom())); +} + +void Element::scrollTo(double x, double y) +{ + scrollTo({ x, y }); } void Element::scrollByUnits(int units, ScrollGranularity granularity) { document().updateLayoutIgnorePendingStylesheets(); - if (!renderer()) + auto* renderer = this->renderer(); + if (!renderer) return; - if (!renderer()->hasOverflowClip()) + if (!renderer->hasOverflowClip()) return; ScrollDirection direction = ScrollDown; @@ -629,7 +747,7 @@ void Element::scrollByUnits(int units, ScrollGranularity granularity) units = -units; } Element* stopElement = this; - toRenderBox(renderer())->scroll(direction, granularity, units, &stopElement); + downcast<RenderBox>(*renderer).scroll(direction, granularity, units, &stopElement); } void Element::scrollByLines(int lines) @@ -642,16 +760,16 @@ void Element::scrollByPages(int pages) scrollByUnits(pages, ScrollByPage); } -static float localZoomForRenderer(RenderElement* renderer) +static double localZoomForRenderer(const RenderElement& renderer) { // FIXME: This does the wrong thing if two opposing zooms are in effect and canceled each // other out, but the alternative is that we'd have to crawl up the whole render tree every // time (or store an additional bit in the RenderStyle to indicate that a zoom was specified). - float zoomFactor = 1; - if (renderer->style().effectiveZoom() != 1) { + double zoomFactor = 1; + if (renderer.style().effectiveZoom() != 1) { // Need to find the nearest enclosing RenderElement that set up // a differing zoom, and then we divide our result by it to eliminate the zoom. - RenderElement* prev = renderer; + const RenderElement* prev = &renderer; for (RenderElement* curr = prev->parent(); curr; curr = curr->parent()) { if (curr->style().effectiveZoom() != prev->style().effectiveZoom()) { zoomFactor = prev->style().zoom(); @@ -665,58 +783,67 @@ static float localZoomForRenderer(RenderElement* renderer) return zoomFactor; } -static int adjustForLocalZoom(LayoutUnit value, RenderElement* renderer) +static double adjustForLocalZoom(LayoutUnit value, const RenderElement& renderer, double& zoomFactor) { - float zoomFactor = localZoomForRenderer(renderer); + zoomFactor = localZoomForRenderer(renderer); if (zoomFactor == 1) - return value; -#if ENABLE(SUBPIXEL_LAYOUT) - return lroundf(value / zoomFactor); -#else - // Needed because computeLengthInt truncates (rather than rounds) when scaling up. - if (zoomFactor > 1) - value++; - return static_cast<int>(value / zoomFactor); -#endif + return value.toDouble(); + return value.toDouble() / zoomFactor; +} + +enum LegacyCSSOMElementMetricsRoundingStrategy { Round, Floor }; + +static bool subpixelMetricsEnabled(const Document& document) +{ + return document.settings().subpixelCSSOMElementMetricsEnabled(); +} + +static double convertToNonSubpixelValueIfNeeded(double value, const Document& document, LegacyCSSOMElementMetricsRoundingStrategy roundStrategy = Round) +{ + return subpixelMetricsEnabled(document) ? value : roundStrategy == Round ? round(value) : floor(value); } -int Element::offsetLeft() +double Element::offsetLeft() { document().updateLayoutIgnorePendingStylesheets(); - if (RenderBoxModelObject* renderer = renderBoxModelObject()) - return adjustForLocalZoom(renderer->pixelSnappedOffsetLeft(), renderer); + if (RenderBoxModelObject* renderer = renderBoxModelObject()) { + LayoutUnit offsetLeft = subpixelMetricsEnabled(renderer->document()) ? renderer->offsetLeft() : LayoutUnit(roundToInt(renderer->offsetLeft())); + double zoomFactor = 1; + double offsetLeftAdjustedWithZoom = adjustForLocalZoom(offsetLeft, *renderer, zoomFactor); + return convertToNonSubpixelValueIfNeeded(offsetLeftAdjustedWithZoom, renderer->document(), zoomFactor == 1 ? Floor : Round); + } return 0; } -int Element::offsetTop() +double Element::offsetTop() { document().updateLayoutIgnorePendingStylesheets(); - if (RenderBoxModelObject* renderer = renderBoxModelObject()) - return adjustForLocalZoom(renderer->pixelSnappedOffsetTop(), renderer); + if (RenderBoxModelObject* renderer = renderBoxModelObject()) { + LayoutUnit offsetTop = subpixelMetricsEnabled(renderer->document()) ? renderer->offsetTop() : LayoutUnit(roundToInt(renderer->offsetTop())); + double zoomFactor = 1; + double offsetTopAdjustedWithZoom = adjustForLocalZoom(offsetTop, *renderer, zoomFactor); + return convertToNonSubpixelValueIfNeeded(offsetTopAdjustedWithZoom, renderer->document(), zoomFactor == 1 ? Floor : Round); + } return 0; } -int Element::offsetWidth() +double Element::offsetWidth() { - document().updateLayoutIgnorePendingStylesheets(); - if (RenderBoxModelObject* renderer = renderBoxModelObject()) -#if ENABLE(SUBPIXEL_LAYOUT) - return adjustLayoutUnitForAbsoluteZoom(renderer->pixelSnappedOffsetWidth(), *renderer).round(); -#else - return adjustForAbsoluteZoom(renderer->pixelSnappedOffsetWidth(), *renderer); -#endif + document().updateLayoutIfDimensionsOutOfDate(*this, WidthDimensionsCheck); + if (RenderBoxModelObject* renderer = renderBoxModelObject()) { + LayoutUnit offsetWidth = subpixelMetricsEnabled(renderer->document()) ? renderer->offsetWidth() : LayoutUnit(roundToInt(renderer->offsetWidth())); + return convertToNonSubpixelValueIfNeeded(adjustLayoutUnitForAbsoluteZoom(offsetWidth, *renderer).toDouble(), renderer->document()); + } return 0; } -int Element::offsetHeight() +double Element::offsetHeight() { - document().updateLayoutIgnorePendingStylesheets(); - if (RenderBoxModelObject* renderer = renderBoxModelObject()) -#if ENABLE(SUBPIXEL_LAYOUT) - return adjustLayoutUnitForAbsoluteZoom(renderer->pixelSnappedOffsetHeight(), *renderer).round(); -#else - return adjustForAbsoluteZoom(renderer->pixelSnappedOffsetHeight(), *renderer); -#endif + document().updateLayoutIfDimensionsOutOfDate(*this, HeightDimensionsCheck); + if (RenderBoxModelObject* renderer = renderBoxModelObject()) { + LayoutUnit offsetHeight = subpixelMetricsEnabled(renderer->document()) ? renderer->offsetHeight() : LayoutUnit(roundToInt(renderer->offsetHeight())); + return convertToNonSubpixelValueIfNeeded(adjustLayoutUnitForAbsoluteZoom(offsetHeight, *renderer).toDouble(), renderer->document()); + } return 0; } @@ -725,7 +852,7 @@ Element* Element::bindingsOffsetParent() Element* element = offsetParent(); if (!element || !element->isInShadowTree()) return element; - return element->containingShadowRoot()->type() == ShadowRoot::UserAgentShadowRoot ? 0 : element; + return element->containingShadowRoot()->mode() == ShadowRootMode::UserAgent ? nullptr : element; } Element* Element::offsetParent() @@ -740,163 +867,124 @@ Element* Element::offsetParent() return offsetParent->element(); } -int Element::clientLeft() +double Element::clientLeft() { document().updateLayoutIgnorePendingStylesheets(); - if (RenderBox* renderer = renderBox()) - return adjustForAbsoluteZoom(roundToInt(renderer->clientLeft()), *renderer); + if (auto* renderer = renderBox()) { + LayoutUnit clientLeft = subpixelMetricsEnabled(renderer->document()) ? renderer->clientLeft() : LayoutUnit(roundToInt(renderer->clientLeft())); + return convertToNonSubpixelValueIfNeeded(adjustLayoutUnitForAbsoluteZoom(clientLeft, *renderer).toDouble(), renderer->document()); + } return 0; } -int Element::clientTop() +double Element::clientTop() { document().updateLayoutIgnorePendingStylesheets(); - if (RenderBox* renderer = renderBox()) - return adjustForAbsoluteZoom(roundToInt(renderer->clientTop()), *renderer); + if (auto* renderer = renderBox()) { + LayoutUnit clientTop = subpixelMetricsEnabled(renderer->document()) ? renderer->clientTop() : LayoutUnit(roundToInt(renderer->clientTop())); + return convertToNonSubpixelValueIfNeeded(adjustLayoutUnitForAbsoluteZoom(clientTop, *renderer).toDouble(), renderer->document()); + } return 0; } -int Element::clientWidth() +double Element::clientWidth() { - document().updateLayoutIgnorePendingStylesheets(); + document().updateLayoutIfDimensionsOutOfDate(*this, WidthDimensionsCheck); if (!document().hasLivingRenderTree()) return 0; + RenderView& renderView = *document().renderView(); // When in strict mode, clientWidth for the document element should return the width of the containing frame. // When in quirks mode, clientWidth for the body element should return the width of the containing frame. bool inQuirksMode = document().inQuirksMode(); - if ((!inQuirksMode && document().documentElement() == this) || (inQuirksMode && isHTMLElement() && document().body() == this)) + if ((!inQuirksMode && document().documentElement() == this) || (inQuirksMode && isHTMLElement() && document().bodyOrFrameset() == this)) return adjustForAbsoluteZoom(renderView.frameView().layoutWidth(), renderView); - if (RenderBox* renderer = renderBox()) -#if ENABLE(SUBPIXEL_LAYOUT) - return adjustLayoutUnitForAbsoluteZoom(renderer->pixelSnappedClientWidth(), *renderer).round(); -#else - return adjustForAbsoluteZoom(renderer->pixelSnappedClientWidth(), *renderer); -#endif + if (RenderBox* renderer = renderBox()) { + LayoutUnit clientWidth = subpixelMetricsEnabled(renderer->document()) ? renderer->clientWidth() : LayoutUnit(roundToInt(renderer->clientWidth())); + return convertToNonSubpixelValueIfNeeded(adjustLayoutUnitForAbsoluteZoom(clientWidth, *renderer).toDouble(), renderer->document()); + } return 0; } -int Element::clientHeight() +double Element::clientHeight() { - document().updateLayoutIgnorePendingStylesheets(); - + document().updateLayoutIfDimensionsOutOfDate(*this, HeightDimensionsCheck); if (!document().hasLivingRenderTree()) return 0; + RenderView& renderView = *document().renderView(); // When in strict mode, clientHeight for the document element should return the height of the containing frame. // When in quirks mode, clientHeight for the body element should return the height of the containing frame. bool inQuirksMode = document().inQuirksMode(); - if ((!inQuirksMode && document().documentElement() == this) || (inQuirksMode && isHTMLElement() && document().body() == this)) + if ((!inQuirksMode && document().documentElement() == this) || (inQuirksMode && isHTMLElement() && document().bodyOrFrameset() == this)) return adjustForAbsoluteZoom(renderView.frameView().layoutHeight(), renderView); - if (RenderBox* renderer = renderBox()) -#if ENABLE(SUBPIXEL_LAYOUT) - return adjustLayoutUnitForAbsoluteZoom(renderer->pixelSnappedClientHeight(), *renderer).round(); -#else - return adjustForAbsoluteZoom(renderer->pixelSnappedClientHeight(), *renderer); -#endif + if (RenderBox* renderer = renderBox()) { + LayoutUnit clientHeight = subpixelMetricsEnabled(renderer->document()) ? renderer->clientHeight() : LayoutUnit(roundToInt(renderer->clientHeight())); + return convertToNonSubpixelValueIfNeeded(adjustLayoutUnitForAbsoluteZoom(clientHeight, *renderer).toDouble(), renderer->document()); + } return 0; } int Element::scrollLeft() { - if (document().documentElement() == this && document().inQuirksMode()) - return 0; - document().updateLayoutIgnorePendingStylesheets(); - if (!document().hasLivingRenderTree()) - return 0; - RenderView& renderView = *document().renderView(); - - if (document().documentElement() == this) - return adjustForAbsoluteZoom(renderView.frameView().scrollX(), renderView); - - if (RenderBox* rend = renderBox()) - return adjustForAbsoluteZoom(rend->scrollLeft(), *rend); + if (auto* renderer = renderBox()) + return adjustForAbsoluteZoom(renderer->scrollLeft(), *renderer); return 0; } int Element::scrollTop() { - if (document().documentElement() == this && document().inQuirksMode()) - return 0; - document().updateLayoutIgnorePendingStylesheets(); - if (!document().hasLivingRenderTree()) - return 0; - RenderView& renderView = *document().renderView(); - - if (document().documentElement() == this) - return adjustForAbsoluteZoom(renderView.frameView().scrollY(), renderView); - - if (RenderBox* rend = renderBox()) - return adjustForAbsoluteZoom(rend->scrollTop(), *rend); + if (RenderBox* renderer = renderBox()) + return adjustForAbsoluteZoom(renderer->scrollTop(), *renderer); return 0; } void Element::setScrollLeft(int newLeft) { - if (document().documentElement() == this && document().inQuirksMode()) - return; - document().updateLayoutIgnorePendingStylesheets(); - if (!document().hasLivingRenderTree()) - return; - - if (document().documentElement() == this) { - RenderView& renderView = *document().renderView(); - int zoom = renderView.style().effectiveZoom(); - renderView.frameView().setScrollPosition(IntPoint(newLeft * zoom, renderView.frameView().scrollY() * zoom)); - return; + if (auto* renderer = renderBox()) { + renderer->setScrollLeft(static_cast<int>(newLeft * renderer->style().effectiveZoom())); + if (auto* scrollableArea = renderer->layer()) + scrollableArea->setScrolledProgrammatically(true); } - - if (RenderBox* rend = renderBox()) - rend->setScrollLeft(static_cast<int>(newLeft * rend->style().effectiveZoom())); } void Element::setScrollTop(int newTop) { - if (document().documentElement() == this && document().inQuirksMode()) - return; - document().updateLayoutIgnorePendingStylesheets(); - if (!document().hasLivingRenderTree()) - return; - - if (document().documentElement() == this) { - RenderView& renderView = *document().renderView(); - int zoom = renderView.style().effectiveZoom(); - renderView.frameView().setScrollPosition(IntPoint(renderView.frameView().scrollX() * zoom, newTop * zoom)); - return; + if (auto* renderer = renderBox()) { + renderer->setScrollTop(static_cast<int>(newTop * renderer->style().effectiveZoom())); + if (auto* scrollableArea = renderer->layer()) + scrollableArea->setScrolledProgrammatically(true); } - - if (RenderBox* rend = renderBox()) - rend->setScrollTop(static_cast<int>(newTop * rend->style().effectiveZoom())); } int Element::scrollWidth() { - document().updateLayoutIgnorePendingStylesheets(); - if (RenderBox* rend = renderBox()) - return adjustForAbsoluteZoom(rend->scrollWidth(), *rend); + document().updateLayoutIfDimensionsOutOfDate(*this, WidthDimensionsCheck); + if (auto* renderer = renderBox()) + return adjustForAbsoluteZoom(renderer->scrollWidth(), *renderer); return 0; } int Element::scrollHeight() { - document().updateLayoutIgnorePendingStylesheets(); - if (RenderBox* rend = renderBox()) - return adjustForAbsoluteZoom(rend->scrollHeight(), *rend); + document().updateLayoutIfDimensionsOutOfDate(*this, HeightDimensionsCheck); + if (auto* renderer = renderBox()) + return adjustForAbsoluteZoom(renderer->scrollHeight(), *renderer); return 0; } @@ -909,16 +997,14 @@ IntRect Element::boundsInRootViewSpace() return IntRect(); Vector<FloatQuad> quads; -#if ENABLE(SVG) + if (isSVGElement() && renderer()) { // Get the bounding rectangle from the SVG model. - SVGElement* svgElement = toSVGElement(this); + SVGElement& svgElement = downcast<SVGElement>(*this); FloatRect localRect; - if (svgElement->getBoundingBox(localRect)) + if (svgElement.getBoundingBox(localRect)) quads.append(renderer()->localToAbsoluteQuad(localRect)); - } else -#endif - { + } else { // Get the bounding rectangle from the box model. if (renderBoxModelObject()) renderBoxModelObject()->absoluteQuads(quads); @@ -935,7 +1021,131 @@ IntRect Element::boundsInRootViewSpace() return result; } -PassRefPtr<ClientRectList> Element::getClientRects() +static bool layoutOverflowRectContainsAllDescendants(const RenderBox& renderBox) +{ + if (renderBox.isRenderView()) + return true; + + if (!renderBox.element()) + return false; + + // If there are any position:fixed inside of us, game over. + if (auto* viewPositionedObjects = renderBox.view().positionedObjects()) { + for (auto* positionedBox : *viewPositionedObjects) { + if (positionedBox == &renderBox) + continue; + if (positionedBox->style().position() == FixedPosition && renderBox.element()->contains(positionedBox->element())) + return false; + } + } + + if (renderBox.canContainAbsolutelyPositionedObjects()) { + // Our layout overflow will include all descendant positioned elements. + return true; + } + + // This renderer may have positioned descendants whose containing block is some ancestor. + if (auto* containingBlock = renderBox.containingBlockForAbsolutePosition()) { + if (auto* positionedObjects = containingBlock->positionedObjects()) { + for (auto* positionedBox : *positionedObjects) { + if (positionedBox == &renderBox) + continue; + if (renderBox.element()->contains(positionedBox->element())) + return false; + } + } + } + return false; +} + +LayoutRect Element::absoluteEventBounds(bool& boundsIncludeAllDescendantElements, bool& includesFixedPositionElements) +{ + boundsIncludeAllDescendantElements = false; + includesFixedPositionElements = false; + + if (!renderer()) + return LayoutRect(); + + LayoutRect result; + if (isSVGElement()) { + // Get the bounding rectangle from the SVG model. + SVGElement& svgElement = downcast<SVGElement>(*this); + FloatRect localRect; + if (svgElement.getBoundingBox(localRect, SVGLocatable::DisallowStyleUpdate)) + result = LayoutRect(renderer()->localToAbsoluteQuad(localRect, UseTransforms, &includesFixedPositionElements).boundingBox()); + } else { + auto* renderer = this->renderer(); + if (is<RenderBox>(renderer)) { + auto& box = downcast<RenderBox>(*renderer); + + bool computedBounds = false; + + if (RenderFlowThread* flowThread = box.flowThreadContainingBlock()) { + bool wasFixed = false; + Vector<FloatQuad> quads; + FloatRect localRect(0, 0, box.width(), box.height()); + if (flowThread->absoluteQuadsForBox(quads, &wasFixed, &box, localRect.y(), localRect.maxY())) { + FloatRect quadBounds = quads[0].boundingBox(); + for (size_t i = 1; i < quads.size(); ++i) + quadBounds.unite(quads[i].boundingBox()); + + result = LayoutRect(quadBounds); + computedBounds = true; + } else { + // Probably columns. Just return the bounds of the multicol block for now. + // FIXME: this doesn't handle nested columns. + RenderElement* multicolContainer = flowThread->parent(); + if (multicolContainer && is<RenderBox>(multicolContainer)) { + auto overflowRect = downcast<RenderBox>(*multicolContainer).layoutOverflowRect(); + result = LayoutRect(multicolContainer->localToAbsoluteQuad(FloatRect(overflowRect), UseTransforms, &includesFixedPositionElements).boundingBox()); + computedBounds = true; + } + } + } + + if (!computedBounds) { + LayoutRect overflowRect = box.layoutOverflowRect(); + result = LayoutRect(box.localToAbsoluteQuad(FloatRect(overflowRect), UseTransforms, &includesFixedPositionElements).boundingBox()); + boundsIncludeAllDescendantElements = layoutOverflowRectContainsAllDescendants(box); + } + } else + result = LayoutRect(renderer->absoluteBoundingBoxRect(true /* useTransforms */, &includesFixedPositionElements)); + } + + return result; +} + +LayoutRect Element::absoluteEventBoundsOfElementAndDescendants(bool& includesFixedPositionElements) +{ + bool boundsIncludeDescendants; + LayoutRect result = absoluteEventBounds(boundsIncludeDescendants, includesFixedPositionElements); + if (boundsIncludeDescendants) + return result; + + for (auto& child : childrenOfType<Element>(*this)) { + bool includesFixedPosition = false; + LayoutRect childBounds = child.absoluteEventBoundsOfElementAndDescendants(includesFixedPosition); + includesFixedPositionElements |= includesFixedPosition; + result.unite(childBounds); + } + + return result; +} + +LayoutRect Element::absoluteEventHandlerBounds(bool& includesFixedPositionElements) +{ + // This is not web-exposed, so don't call the FOUC-inducing updateLayoutIgnorePendingStylesheets(). + FrameView* frameView = document().view(); + if (!frameView) + return LayoutRect(); + + if (frameView->needsLayout()) + frameView->layout(); + + return absoluteEventBoundsOfElementAndDescendants(includesFixedPositionElements); +} + +Ref<ClientRectList> Element::getClientRects() { document().updateLayoutIgnorePendingStylesheets(); @@ -952,21 +1162,18 @@ PassRefPtr<ClientRectList> Element::getClientRects() return ClientRectList::create(quads); } -PassRefPtr<ClientRect> Element::getBoundingClientRect() +Ref<ClientRect> Element::getBoundingClientRect() { document().updateLayoutIgnorePendingStylesheets(); Vector<FloatQuad> quads; -#if ENABLE(SVG) if (isSVGElement() && renderer() && !renderer()->isSVGRoot()) { // Get the bounding rectangle from the SVG model. - SVGElement* svgElement = toSVGElement(this); + SVGElement& svgElement = downcast<SVGElement>(*this); FloatRect localRect; - if (svgElement->getBoundingBox(localRect)) + if (svgElement.getBoundingBox(localRect)) quads.append(renderer()->localToAbsoluteQuad(localRect)); - } else -#endif - { + } else { // Get the bounding rectangle from the box model. if (renderBoxModelObject()) renderBoxModelObject()->absoluteQuads(quads); @@ -1012,19 +1219,18 @@ const AtomicString& Element::getAttributeNS(const AtomicString& namespaceURI, co return getAttribute(QualifiedName(nullAtom, localName, namespaceURI)); } -void Element::setAttribute(const AtomicString& localName, const AtomicString& value, ExceptionCode& ec) +ExceptionOr<void> Element::setAttribute(const AtomicString& localName, const AtomicString& value) { - if (!Document::isValidName(localName)) { - ec = INVALID_CHARACTER_ERR; - return; - } + if (!Document::isValidName(localName)) + return Exception { INVALID_CHARACTER_ERR }; synchronizeAttribute(localName); - const AtomicString& caseAdjustedLocalName = shouldIgnoreAttributeCase(*this) ? localName.lower() : localName; - + auto caseAdjustedLocalName = shouldIgnoreAttributeCase(*this) ? localName.convertToASCIILowercase() : localName; unsigned index = elementData() ? elementData()->findAttributeIndexByName(caseAdjustedLocalName, false) : ElementData::attributeNotFound; - const QualifiedName& qName = index != ElementData::attributeNotFound ? attributeAt(index).name() : QualifiedName(nullAtom, caseAdjustedLocalName, nullAtom); - setAttributeInternal(index, qName, value, NotInSynchronizationOfLazyAttribute); + auto name = index != ElementData::attributeNotFound ? attributeAt(index).name() : QualifiedName { nullAtom, caseAdjustedLocalName, nullAtom }; + setAttributeInternal(index, name, value, NotInSynchronizationOfLazyAttribute); + + return { }; } void Element::setAttribute(const QualifiedName& name, const AtomicString& value) @@ -1034,6 +1240,12 @@ void Element::setAttribute(const QualifiedName& name, const AtomicString& value) setAttributeInternal(index, name, value, NotInSynchronizationOfLazyAttribute); } +void Element::setAttributeWithoutSynchronization(const QualifiedName& name, const AtomicString& value) +{ + unsigned index = elementData() ? elementData()->findAttributeIndexByName(name) : ElementData::attributeNotFound; + setAttributeInternal(index, name, value, NotInSynchronizationOfLazyAttribute); +} + void Element::setSynchronizedLazyAttribute(const QualifiedName& name, const AtomicString& value) { unsigned index = elementData() ? elementData()->findAttributeIndexByName(name) : ElementData::attributeNotFound; @@ -1053,85 +1265,83 @@ inline void Element::setAttributeInternal(unsigned index, const QualifiedName& n return; } + if (inSynchronizationOfLazyAttribute) { + ensureUniqueElementData().attributeAt(index).setValue(newValue); + return; + } + const Attribute& attribute = attributeAt(index); + QualifiedName attributeName = attribute.name(); AtomicString oldValue = attribute.value(); - bool valueChanged = newValue != oldValue; - QualifiedName attributeName = (!inSynchronizationOfLazyAttribute || valueChanged) ? attribute.name() : name; - if (!inSynchronizationOfLazyAttribute) - willModifyAttribute(attributeName, oldValue, newValue); + willModifyAttribute(attributeName, oldValue, newValue); - if (valueChanged) { + if (newValue != oldValue) { // If there is an Attr node hooked to this attribute, the Attr::setValue() call below // will write into the ElementData. // FIXME: Refactor this so it makes some sense. - if (RefPtr<Attr> attrNode = inSynchronizationOfLazyAttribute ? 0 : attrIfExists(attributeName)) + if (RefPtr<Attr> attrNode = attrIfExists(attributeName)) attrNode->setValue(newValue); - else + else { + Style::AttributeChangeInvalidation styleInvalidation(*this, name, oldValue, newValue); ensureUniqueElementData().attributeAt(index).setValue(newValue); + } } - if (!inSynchronizationOfLazyAttribute) - didModifyAttribute(attributeName, oldValue, newValue); + didModifyAttribute(attributeName, oldValue, newValue); } static inline AtomicString makeIdForStyleResolution(const AtomicString& value, bool inQuirksMode) { if (inQuirksMode) - return value.lower(); + return value.convertToASCIILowercase(); return value; } -static bool checkNeedsStyleInvalidationForIdChange(const AtomicString& oldId, const AtomicString& newId, StyleResolver* styleResolver) -{ - ASSERT(newId != oldId); - if (!oldId.isEmpty() && styleResolver->hasSelectorForId(oldId)) - return true; - if (!newId.isEmpty() && styleResolver->hasSelectorForId(newId)) - return true; - return false; -} - void Element::attributeChanged(const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue, AttributeModificationReason) { - parseAttribute(name, newValue); - - document().incDOMTreeVersion(); + bool valueIsSameAsBefore = oldValue == newValue; - if (oldValue == newValue) - return; + if (!valueIsSameAsBefore) { + if (name == HTMLNames::idAttr) { + if (!oldValue.isEmpty()) + treeScope().idTargetObserverRegistry().notifyObservers(*oldValue.impl()); + if (!newValue.isEmpty()) + treeScope().idTargetObserverRegistry().notifyObservers(*newValue.impl()); - StyleResolver* styleResolver = document().styleResolverIfExists(); - bool testShouldInvalidateStyle = inRenderedDocument() && styleResolver && styleChangeType() < FullStyleChange; - bool shouldInvalidateStyle = false; - - if (isIdAttributeName(name)) { - if (!oldValue.isEmpty()) - treeScope().idTargetObserverRegistry().notifyObservers(*oldValue.impl()); - if (!newValue.isEmpty()) - treeScope().idTargetObserverRegistry().notifyObservers(*newValue.impl()); - - AtomicString oldId = elementData()->idForStyleResolution(); - AtomicString newId = makeIdForStyleResolution(newValue, document().inQuirksMode()); - if (newId != oldId) { - elementData()->setIdForStyleResolution(newId); - shouldInvalidateStyle = testShouldInvalidateStyle && checkNeedsStyleInvalidationForIdChange(oldId, newId, styleResolver); + AtomicString oldId = elementData()->idForStyleResolution(); + AtomicString newId = makeIdForStyleResolution(newValue, document().inQuirksMode()); + if (newId != oldId) { + Style::IdChangeInvalidation styleInvalidation(*this, oldId, newId); + elementData()->setIdForStyleResolution(newId); + } + } else if (name == classAttr) + classAttributeChanged(newValue); + else if (name == HTMLNames::nameAttr) + elementData()->setHasNameAttribute(!newValue.isNull()); + else if (name == HTMLNames::pseudoAttr) { + if (needsStyleInvalidation() && isInShadowTree()) + invalidateStyleForSubtree(); } - } else if (name == classAttr) - classAttributeChanged(newValue); - else if (name == HTMLNames::nameAttr) - elementData()->setHasNameAttribute(!newValue.isNull()); - else if (name == HTMLNames::pseudoAttr) - shouldInvalidateStyle |= testShouldInvalidateStyle && isInShadowTree(); + else if (name == HTMLNames::slotAttr) { + if (auto* parent = parentElement()) { + if (auto* shadowRoot = parent->shadowRoot()) + shadowRoot->hostChildElementDidChangeSlotAttribute(*this, oldValue, newValue); + } + } + } + parseAttribute(name, newValue); - invalidateNodeListAndCollectionCachesInAncestors(&name, this); + document().incDOMTreeVersion(); - // If there is currently no StyleResolver, we can't be sure that this attribute change won't affect style. - shouldInvalidateStyle |= !styleResolver; + if (UNLIKELY(isDefinedCustomElement())) + CustomElementReactionQueue::enqueueAttributeChangedCallbackIfNeeded(*this, name, oldValue, newValue); - if (shouldInvalidateStyle) - setNeedsStyleRecalc(); + if (valueIsSameAsBefore) + return; + + invalidateNodeListAndCollectionCachesInAncestors(&name, this); if (AXObjectCache* cache = document().existingAXObjectCache()) cache->handleAttributeChanged(name, this); @@ -1164,74 +1374,114 @@ static inline bool classStringHasClassName(const AtomicString& newClassString) return classStringHasClassName(newClassString.characters16(), length); } -static bool checkSelectorForClassChange(const SpaceSplitString& changedClasses, const StyleResolver& styleResolver) +void Element::classAttributeChanged(const AtomicString& newClassString) { - unsigned changedSize = changedClasses.size(); - for (unsigned i = 0; i < changedSize; ++i) { - if (styleResolver.hasSelectorForClass(changedClasses[i])) - return true; - } - return false; -} + // Note: We'll need ElementData, but it doesn't have to be UniqueElementData. + if (!elementData()) + ensureUniqueElementData(); -static bool checkSelectorForClassChange(const SpaceSplitString& oldClasses, const SpaceSplitString& newClasses, const StyleResolver& styleResolver) -{ - unsigned oldSize = oldClasses.size(); - if (!oldSize) - return checkSelectorForClassChange(newClasses, styleResolver); - BitVector remainingClassBits; - remainingClassBits.ensureSize(oldSize); - // Class vectors tend to be very short. This is faster than using a hash table. - unsigned newSize = newClasses.size(); - for (unsigned i = 0; i < newSize; ++i) { - bool foundFromBoth = false; - for (unsigned j = 0; j < oldSize; ++j) { - if (newClasses[i] == oldClasses[j]) { - remainingClassBits.quickSet(j); - foundFromBoth = true; - } - } - if (foundFromBoth) - continue; - if (styleResolver.hasSelectorForClass(newClasses[i])) - return true; + bool shouldFoldCase = document().inQuirksMode(); + bool newStringHasClasses = classStringHasClassName(newClassString); + + auto oldClassNames = elementData()->classNames(); + auto newClassNames = newStringHasClasses ? SpaceSplitString(newClassString, shouldFoldCase) : SpaceSplitString(); + { + Style::ClassChangeInvalidation styleInvalidation(*this, oldClassNames, newClassNames); + elementData()->setClassNames(newClassNames); } - for (unsigned i = 0; i < oldSize; ++i) { - // If the bit is not set the the corresponding class has been removed. - if (remainingClassBits.quickGet(i)) - continue; - if (styleResolver.hasSelectorForClass(oldClasses[i])) - return true; + + if (hasRareData()) { + if (auto* classList = elementRareData()->classList()) + classList->associatedAttributeValueChanged(newClassString); } - return false; } -void Element::classAttributeChanged(const AtomicString& newClassString) +URL Element::absoluteLinkURL() const { - StyleResolver* styleResolver = document().styleResolverIfExists(); - bool testShouldInvalidateStyle = inRenderedDocument() && styleResolver && styleChangeType() < FullStyleChange; - bool shouldInvalidateStyle = false; - - if (classStringHasClassName(newClassString)) { - const bool shouldFoldCase = document().inQuirksMode(); - // Note: We'll need ElementData, but it doesn't have to be UniqueElementData. - if (!elementData()) - ensureUniqueElementData(); - const SpaceSplitString oldClasses = elementData()->classNames(); - elementData()->setClass(newClassString, shouldFoldCase); - const SpaceSplitString& newClasses = elementData()->classNames(); - shouldInvalidateStyle = testShouldInvalidateStyle && checkSelectorForClassChange(oldClasses, newClasses, *styleResolver); - } else if (elementData()) { - const SpaceSplitString& oldClasses = elementData()->classNames(); - shouldInvalidateStyle = testShouldInvalidateStyle && checkSelectorForClassChange(oldClasses, *styleResolver); - elementData()->clearClass(); - } + if (!isLink()) + return URL(); - if (hasRareData()) - elementRareData()->clearClassListValueForQuirksMode(); + AtomicString linkAttribute; + if (hasTagName(SVGNames::aTag)) + linkAttribute = getAttribute(XLinkNames::hrefAttr); + else + linkAttribute = getAttribute(HTMLNames::hrefAttr); + + if (linkAttribute.isEmpty()) + return URL(); - if (shouldInvalidateStyle) - setNeedsStyleRecalc(); + return document().completeURL(stripLeadingAndTrailingHTMLSpaces(linkAttribute)); +} + +#if ENABLE(TOUCH_EVENTS) +bool Element::allowsDoubleTapGesture() const +{ + if (renderStyle() && renderStyle()->touchAction() != TouchAction::Auto) + return false; + + Element* parent = parentElement(); + return !parent || parent->allowsDoubleTapGesture(); +} +#endif + +StyleResolver& Element::styleResolver() +{ + if (auto* shadowRoot = containingShadowRoot()) + return shadowRoot->styleScope().resolver(); + + return document().styleScope().resolver(); +} + +ElementStyle Element::resolveStyle(const RenderStyle* parentStyle) +{ + return styleResolver().styleForElement(*this, parentStyle); +} + +void Element::invalidateStyle() +{ + Node::invalidateStyle(Style::Validity::ElementInvalid); +} + +void Element::invalidateStyleAndLayerComposition() +{ + Node::invalidateStyle(Style::Validity::ElementInvalid, Style::InvalidationMode::RecompositeLayer); +} + +void Element::invalidateStyleForSubtree() +{ + Node::invalidateStyle(Style::Validity::SubtreeInvalid); +} + +void Element::invalidateStyleAndRenderersForSubtree() +{ + Node::invalidateStyle(Style::Validity::SubtreeAndRenderersInvalid); +} + +#if ENABLE(WEB_ANIMATIONS) +WebAnimationVector Element::getAnimations() +{ + auto checkTarget = [this](AnimationEffect const& effect) + { + return (static_cast<KeyframeEffect const&>(effect).target() == this); + }; + + auto* document = DocumentAnimation::from(&this->document()); + if (document) + return document->getAnimations(checkTarget); + return WebAnimationVector(); +} +#endif + +bool Element::hasDisplayContents() const +{ + return hasRareData() && elementRareData()->hasDisplayContents(); +} + +void Element::setHasDisplayContents(bool value) +{ + if (hasDisplayContents() == value) + return; + ensureElementRareData().setHasDisplayContents(value); } // Returns true is the given attribute is an event handler. @@ -1251,38 +1501,52 @@ bool Element::isJavaScriptURLAttribute(const Attribute& attribute) const void Element::stripScriptingAttributes(Vector<Attribute>& attributeVector) const { - size_t destination = 0; - for (size_t source = 0; source < attributeVector.size(); ++source) { - if (isEventHandlerAttribute(attributeVector[source]) - || isJavaScriptURLAttribute(attributeVector[source]) - || isHTMLContentAttribute(attributeVector[source])) - continue; - - if (source != destination) - attributeVector[destination] = attributeVector[source]; - - ++destination; - } - attributeVector.shrink(destination); + attributeVector.removeAllMatching([this](auto& attribute) -> bool { + return isEventHandlerAttribute(attribute) + || this->isJavaScriptURLAttribute(attribute) + || this->isHTMLContentAttribute(attribute); + }); } void Element::parserSetAttributes(const Vector<Attribute>& attributeVector) { - ASSERT(!inDocument()); + ASSERT(!isConnected()); ASSERT(!parentNode()); ASSERT(!m_elementData); - if (attributeVector.isEmpty()) - return; + if (!attributeVector.isEmpty()) { + if (document().sharedObjectPool()) + m_elementData = document().sharedObjectPool()->cachedShareableElementDataWithAttributes(attributeVector); + else + m_elementData = ShareableElementData::createWithAttributes(attributeVector); - if (document().sharedObjectPool()) - m_elementData = document().sharedObjectPool()->cachedShareableElementDataWithAttributes(attributeVector); - else - m_elementData = ShareableElementData::createWithAttributes(attributeVector); + } + + parserDidSetAttributes(); // Use attributeVector instead of m_elementData because attributeChanged might modify m_elementData. - for (unsigned i = 0; i < attributeVector.size(); ++i) - attributeChanged(attributeVector[i].name(), nullAtom, attributeVector[i].value(), ModifiedDirectly); + for (const auto& attribute : attributeVector) + attributeChanged(attribute.name(), nullAtom, attribute.value(), ModifiedDirectly); +} + +void Element::parserDidSetAttributes() +{ +} + +void Element::didMoveToNewDocument(Document& oldDocument) +{ + Node::didMoveToNewDocument(oldDocument); + + if (oldDocument.inQuirksMode() != document().inQuirksMode()) { + // ElementData::m_classNames or ElementData::m_idForStyleResolution need to be updated with the right case. + if (hasID()) + attributeChanged(idAttr, nullAtom, getIdAttribute()); + if (hasClass()) + attributeChanged(classAttr, nullAtom, getAttribute(classAttr)); + } + + if (UNLIKELY(isDefinedCustomElement())) + CustomElementReactionQueue::enqueueAdoptedCallbackIfNeeded(*this, oldDocument, document()); } bool Element::hasAttributes() const @@ -1314,72 +1578,60 @@ String Element::nodeNamePreservingCase() const return m_tagName.toString(); } -void Element::setPrefix(const AtomicString& prefix, ExceptionCode& ec) +ExceptionOr<void> Element::setPrefix(const AtomicString& prefix) { - ec = 0; - checkSetPrefix(prefix, ec); - if (ec) - return; + auto result = checkSetPrefix(prefix); + if (result.hasException()) + return result.releaseException(); - m_tagName.setPrefix(prefix.isEmpty() ? AtomicString() : prefix); -} - -URL Element::baseURI() const -{ - const AtomicString& baseAttribute = getAttribute(baseAttr); - URL base(URL(), baseAttribute); - if (!base.protocol().isEmpty()) - return base; - - ContainerNode* parent = parentNode(); - if (!parent) - return base; - - const URL& parentBase = parent->baseURI(); - if (parentBase.isNull()) - return base; - - return URL(parentBase, baseAttribute); + m_tagName.setPrefix(prefix.isEmpty() ? nullAtom : prefix); + return { }; } const AtomicString& Element::imageSourceURL() const { - return getAttribute(srcAttr); + return attributeWithoutSynchronization(srcAttr); } bool Element::rendererIsNeeded(const RenderStyle& style) { - return style.display() != NONE; + return style.display() != NONE && style.display() != CONTENTS; } -RenderPtr<RenderElement> Element::createElementRenderer(PassRef<RenderStyle> style) +RenderPtr<RenderElement> Element::createElementRenderer(RenderStyle&& style, const RenderTreePosition&) { - return RenderElement::createFor(*this, std::move(style)); + return RenderElement::createFor(*this, WTFMove(style)); } Node::InsertionNotificationRequest Element::insertedInto(ContainerNode& insertionPoint) { - bool wasInDocument = inDocument(); - // need to do superclass processing first so inDocument() is true + bool wasInDocument = isConnected(); + // need to do superclass processing first so isConnected() is true // by the time we reach updateId ContainerNode::insertedInto(insertionPoint); - ASSERT(!wasInDocument || inDocument()); + ASSERT(!wasInDocument || isConnected()); #if ENABLE(FULLSCREEN_API) if (containsFullScreenElement() && parentElement() && !parentElement()->containsFullScreenElement()) setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(true); #endif + if (parentNode() == &insertionPoint) { + if (auto* shadowRoot = parentNode()->shadowRoot()) + shadowRoot->hostChildElementDidChange(*this); + } + if (!insertionPoint.isInTreeScope()) return InsertionDone; - if (hasRareData()) - elementRareData()->clearClassListValueForQuirksMode(); - + // This function could be called when this element's shadow root's host or its ancestor is inserted. + // This element is new to the shadow tree (and its tree scope) only if the parent into which this element + // or its ancestor is inserted belongs to the same tree scope as this element's. TreeScope* newScope = &insertionPoint.treeScope(); - HTMLDocument* newDocument = !wasInDocument && inDocument() && newScope->documentScope()->isHTMLDocument() ? toHTMLDocument(newScope->documentScope()) : 0; + bool becomeConnected = !wasInDocument && isConnected(); + HTMLDocument* newDocument = becomeConnected && is<HTMLDocument>(newScope->documentScope()) ? &downcast<HTMLDocument>(newScope->documentScope()) : nullptr; if (newScope != &treeScope()) - newScope = 0; + newScope = nullptr; const AtomicString& idValue = getIdAttribute(); if (!idValue.isNull()) { @@ -1399,7 +1651,14 @@ Node::InsertionNotificationRequest Element::insertedInto(ContainerNode& insertio if (newScope && hasTagName(labelTag)) { if (newScope->shouldCacheLabelsByForAttribute()) - updateLabel(*newScope, nullAtom, fastGetAttribute(forAttr)); + updateLabel(*newScope, nullAtom, attributeWithoutSynchronization(forAttr)); + } + + if (becomeConnected) { + if (UNLIKELY(isCustomElementUpgradeCandidate())) + CustomElementReactionQueue::enqueueElementUpgradeIfDefined(*this); + if (UNLIKELY(isDefinedCustomElement())) + CustomElementReactionQueue::enqueueConnectedCallbackIfNeeded(*this); } return InsertionDone; @@ -1413,16 +1672,20 @@ void Element::removedFrom(ContainerNode& insertionPoint) #endif #if ENABLE(POINTER_LOCK) if (document().page()) - document().page()->pointerLockController()->elementRemoved(this); + document().page()->pointerLockController().elementRemoved(*this); #endif - setSavedLayerScrollOffset(IntSize()); + setSavedLayerScrollPosition(ScrollPosition()); if (insertionPoint.isInTreeScope()) { TreeScope* oldScope = &insertionPoint.treeScope(); - HTMLDocument* oldDocument = inDocument() && oldScope->documentScope()->isHTMLDocument() ? toHTMLDocument(oldScope->documentScope()) : 0; - if (oldScope != &treeScope()) - oldScope = 0; + bool becomeDisconnected = isConnected(); + HTMLDocument* oldDocument = becomeDisconnected && is<HTMLDocument>(oldScope->documentScope()) ? &downcast<HTMLDocument>(oldScope->documentScope()) : nullptr; + + // ContainerNode::removeBetween always sets the removed chid's tree scope to Document's but InTreeScope flag is unset in Node::removedFrom. + // So this element has been removed from the old tree scope only if InTreeScope flag is set and this element's tree scope is Document's. + if (!isInTreeScope() || &treeScope() != &document()) + oldScope = nullptr; const AtomicString& idValue = getIdAttribute(); if (!idValue.isNull()) { @@ -1442,75 +1705,65 @@ void Element::removedFrom(ContainerNode& insertionPoint) if (oldScope && hasTagName(labelTag)) { if (oldScope->shouldCacheLabelsByForAttribute()) - updateLabel(*oldScope, fastGetAttribute(forAttr), nullAtom); + updateLabel(*oldScope, attributeWithoutSynchronization(forAttr), nullAtom); } + + if (becomeDisconnected && UNLIKELY(isDefinedCustomElement())) + CustomElementReactionQueue::enqueueDisconnectedCallbackIfNeeded(*this); + } + + if (!parentNode()) { + if (auto* shadowRoot = insertionPoint.shadowRoot()) + shadowRoot->hostChildElementDidChange(*this); } ContainerNode::removedFrom(insertionPoint); -#if ENABLE(SVG) + if (hasPendingResources()) - document().accessSVGExtensions()->removeElementFromPendingResources(this); + document().accessSVGExtensions().removeElementFromPendingResources(this); + + +#if PLATFORM(MAC) + if (Frame* frame = document().frame()) + frame->mainFrame().removeLatchingStateForTarget(*this); #endif } void Element::unregisterNamedFlowContentElement() { - if (document().cssRegionsEnabled() && inNamedFlow() && document().renderView()) + if (isNamedFlowContentElement() && document().renderView()) document().renderView()->flowThreadController().unregisterNamedFlowContentElement(*this); } -PassRef<RenderStyle> Element::styleForRenderer() -{ - if (hasCustomStyleResolveCallbacks()) { - if (RefPtr<RenderStyle> style = customStyleForRenderer()) - return style.releaseNonNull(); - } - - return document().ensureStyleResolver().styleForElement(this); -} - ShadowRoot* Element::shadowRoot() const { - return hasRareData() ? elementRareData()->shadowRoot() : 0; -} - -void Element::didAffectSelector(AffectedSelectorMask) -{ - setNeedsStyleRecalc(); -} - -static bool shouldUseNodeRenderingTraversalSlowPath(const Element& element) -{ - if (element.isShadowRoot()) - return true; - if (element.isPseudoElement() || element.beforePseudoElement() || element.afterPseudoElement()) - return true; - return element.isInsertionPoint() || element.shadowRoot(); -} - -void Element::resetNeedsNodeRenderingTraversalSlowPath() -{ - setNeedsNodeRenderingTraversalSlowPath(shouldUseNodeRenderingTraversalSlowPath(*this)); + return hasRareData() ? elementRareData()->shadowRoot() : nullptr; } -void Element::addShadowRoot(PassRefPtr<ShadowRoot> newShadowRoot) +void Element::addShadowRoot(Ref<ShadowRoot>&& newShadowRoot) { ASSERT(!shadowRoot()); + + if (renderer()) + RenderTreeUpdater::tearDownRenderers(*this); - ShadowRoot* shadowRoot = newShadowRoot.get(); - ensureElementRareData().setShadowRoot(newShadowRoot); + ShadowRoot& shadowRoot = newShadowRoot; + ensureElementRareData().setShadowRoot(WTFMove(newShadowRoot)); - shadowRoot->setHostElement(this); - shadowRoot->setParentTreeScope(&treeScope()); - shadowRoot->distributor().didShadowBoundaryChange(this); + shadowRoot.setHost(this); + shadowRoot.setParentTreeScope(treeScope()); - ChildNodeInsertionNotifier(*this).notify(*shadowRoot); + NodeVector postInsertionNotificationTargets; + notifyChildNodeInserted(*this, shadowRoot, postInsertionNotificationTargets); + for (auto& target : postInsertionNotificationTargets) + target->finishedInsertingSubtree(); - resetNeedsNodeRenderingTraversalSlowPath(); + invalidateStyleAndRenderersForSubtree(); - setNeedsStyleRecalc(ReconstructRenderTree); + InspectorInstrumentation::didPushShadowRoot(*this, shadowRoot); - InspectorInstrumentation::didPushShadowRoot(this, shadowRoot); + if (shadowRoot.mode() == ShadowRootMode::UserAgent) + didAddUserAgentShadowRoot(&shadowRoot); } void Element::removeShadowRoot() @@ -1518,71 +1771,147 @@ void Element::removeShadowRoot() RefPtr<ShadowRoot> oldRoot = shadowRoot(); if (!oldRoot) return; - InspectorInstrumentation::willPopShadowRoot(this, oldRoot.get()); - document().removeFocusedNodeOfSubtree(oldRoot.get()); + + InspectorInstrumentation::willPopShadowRoot(*this, *oldRoot); + document().removeFocusedNodeOfSubtree(*oldRoot); ASSERT(!oldRoot->renderer()); elementRareData()->clearShadowRoot(); - oldRoot->setHostElement(0); - oldRoot->setParentTreeScope(&document()); - - ChildNodeRemovalNotifier(*this).notify(*oldRoot); + oldRoot->setHost(nullptr); + oldRoot->setParentTreeScope(document()); +} + +static bool canAttachAuthorShadowRoot(const Element& element) +{ + static NeverDestroyed<HashSet<AtomicString>> tagNames = [] { + static const HTMLQualifiedName* const tagList[] = { + &articleTag, + &asideTag, + &blockquoteTag, + &bodyTag, + &divTag, + &footerTag, + &h1Tag, + &h2Tag, + &h3Tag, + &h4Tag, + &h5Tag, + &h6Tag, + &headerTag, + &navTag, + &pTag, + §ionTag, + &spanTag + }; + HashSet<AtomicString> set; + for (auto& name : tagList) + set.add(name->localName()); + return set; + }(); + + if (!is<HTMLElement>(element)) + return false; - oldRoot->distributor().invalidateDistribution(this); + const auto& localName = element.localName(); + return tagNames.get().contains(localName) || Document::validateCustomElementName(localName) == CustomElementNameValidationStatus::Valid; } -PassRefPtr<ShadowRoot> Element::createShadowRoot(ExceptionCode& ec) +ExceptionOr<ShadowRoot&> Element::attachShadow(const ShadowRootInit& init) { - if (alwaysCreateUserAgentShadowRoot()) - ensureUserAgentShadowRoot(); - -#if ENABLE(SHADOW_DOM) - if (RuntimeEnabledFeatures::sharedFeatures().authorShadowDOMForAnyElementEnabled()) { - addShadowRoot(ShadowRoot::create(document(), ShadowRoot::AuthorShadowRoot)); - return shadowRoot(); - } -#endif + if (!canAttachAuthorShadowRoot(*this)) + return Exception { NOT_SUPPORTED_ERR }; + if (shadowRoot()) + return Exception { INVALID_STATE_ERR }; + if (init.mode == ShadowRootMode::UserAgent) + return Exception { TypeError }; + auto shadow = ShadowRoot::create(document(), init.mode); + auto& result = shadow.get(); + addShadowRoot(WTFMove(shadow)); + return result; +} - // Since some elements recreates shadow root dynamically, multiple shadow - // subtrees won't work well in that element. Until they are fixed, we disable - // adding author shadow root for them. - if (!areAuthorShadowsAllowed()) { - ec = HIERARCHY_REQUEST_ERR; - return 0; - } - addShadowRoot(ShadowRoot::create(document(), ShadowRoot::AuthorShadowRoot)); +ShadowRoot* Element::shadowRootForBindings(JSC::ExecState& state) const +{ + auto* shadow = shadowRoot(); + if (!shadow) + return nullptr; + if (shadow->mode() == ShadowRootMode::Open) + return shadow; + if (JSC::jsCast<JSDOMGlobalObject*>(state.lexicalGlobalObject())->world().shadowRootIsAlwaysOpen()) + return shadow; + return nullptr; +} +ShadowRoot* Element::userAgentShadowRoot() const +{ + ASSERT(!shadowRoot() || shadowRoot()->mode() == ShadowRootMode::UserAgent); return shadowRoot(); } -ShadowRoot* Element::authorShadowRoot() const +ShadowRoot& Element::ensureUserAgentShadowRoot() { - ShadowRoot* shadowRoot = this->shadowRoot(); - if (shadowRoot->type() == ShadowRoot::AuthorShadowRoot) - return shadowRoot; - return 0; + if (auto* shadow = userAgentShadowRoot()) + return *shadow; + auto newShadow = ShadowRoot::create(document(), ShadowRootMode::UserAgent); + ShadowRoot& shadow = newShadow; + addShadowRoot(WTFMove(newShadow)); + return shadow; } -ShadowRoot* Element::userAgentShadowRoot() const +void Element::setIsDefinedCustomElement(JSCustomElementInterface& elementInterface) { - if (ShadowRoot* shadowRoot = this->shadowRoot()) { - ASSERT(shadowRoot->type() == ShadowRoot::UserAgentShadowRoot); - return shadowRoot; - } - return 0; + clearFlag(IsEditingTextOrUndefinedCustomElementFlag); + setFlag(IsCustomElement); + auto& data = ensureElementRareData(); + if (!data.customElementReactionQueue()) + data.setCustomElementReactionQueue(std::make_unique<CustomElementReactionQueue>(elementInterface)); + InspectorInstrumentation::didChangeCustomElementState(*this); } -ShadowRoot& Element::ensureUserAgentShadowRoot() +void Element::setIsFailedCustomElement(JSCustomElementInterface&) { - ShadowRoot* shadowRoot = userAgentShadowRoot(); - if (!shadowRoot) { - addShadowRoot(ShadowRoot::create(document(), ShadowRoot::UserAgentShadowRoot)); - shadowRoot = userAgentShadowRoot(); - didAddUserAgentShadowRoot(shadowRoot); + ASSERT(isUndefinedCustomElement()); + ASSERT(getFlag(IsEditingTextOrUndefinedCustomElementFlag)); + clearFlag(IsCustomElement); + + if (hasRareData()) { + // Clear the queue instead of deleting it since this function can be called inside CustomElementReactionQueue::invokeAll during upgrades. + if (auto* queue = elementRareData()->customElementReactionQueue()) + queue->clear(); } - return *shadowRoot; + InspectorInstrumentation::didChangeCustomElementState(*this); +} + +void Element::setIsCustomElementUpgradeCandidate() +{ + ASSERT(!getFlag(IsCustomElement)); + setFlag(IsCustomElement); + setFlag(IsEditingTextOrUndefinedCustomElementFlag); + InspectorInstrumentation::didChangeCustomElementState(*this); +} + +void Element::enqueueToUpgrade(JSCustomElementInterface& elementInterface) +{ + ASSERT(!isDefinedCustomElement() && !isFailedCustomElement()); + setFlag(IsCustomElement); + setFlag(IsEditingTextOrUndefinedCustomElementFlag); + InspectorInstrumentation::didChangeCustomElementState(*this); + + auto& data = ensureElementRareData(); + ASSERT(!data.customElementReactionQueue()); + + data.setCustomElementReactionQueue(std::make_unique<CustomElementReactionQueue>(elementInterface)); + data.customElementReactionQueue()->enqueueElementUpgrade(*this); +} + +CustomElementReactionQueue* Element::reactionQueue() const +{ + ASSERT(isDefinedCustomElement() || isCustomElementUpgradeCandidate()); + if (!hasRareData()) + return nullptr; + return elementRareData()->customElementReactionQueue(); } const AtomicString& Element::shadowPseudoId() const @@ -1598,7 +1927,6 @@ bool Element::childTypeAllowed(NodeType type) const case COMMENT_NODE: case PROCESSING_INSTRUCTION_NODE: case CDATA_SECTION_NODE: - case ENTITY_REFERENCE_NODE: return true; default: break; @@ -1606,90 +1934,138 @@ bool Element::childTypeAllowed(NodeType type) const return false; } -static void checkForEmptyStyleChange(Element* element, RenderStyle* style) +static void checkForEmptyStyleChange(Element& element) { - if (!style && !element->styleAffectedByEmpty()) - return; - - if (!style || (element->styleAffectedByEmpty() && (!style->emptyState() || element->hasChildNodes()))) - element->setNeedsStyleRecalc(); + if (element.styleAffectedByEmpty()) { + auto* style = element.renderStyle(); + if (!style || (!style->emptyState() || element.hasChildNodes())) + element.invalidateStyleForSubtree(); + } } enum SiblingCheckType { FinishedParsingChildren, SiblingElementRemoved, Other }; -static void checkForSiblingStyleChanges(Element* parent, SiblingCheckType checkType, Element* elementBeforeChange, Element* elementAfterChange) +static void checkForSiblingStyleChanges(Element& parent, SiblingCheckType checkType, Element* elementBeforeChange, Element* elementAfterChange) { - RenderStyle* style = parent->renderStyle(); // :empty selector. - checkForEmptyStyleChange(parent, style); - - if (!style || (parent->needsStyleRecalc() && parent->childrenAffectedByPositionalRules())) + checkForEmptyStyleChange(parent); + + if (parent.styleValidity() >= Style::Validity::SubtreeInvalid) return; // :first-child. In the parser callback case, we don't have to check anything, since we were right the first time. // In the DOM case, we only need to do something if |afterChange| is not 0. // |afterChange| is 0 in the parser case, so it works out that we'll skip this block. - if (parent->childrenAffectedByFirstChildRules() && elementAfterChange) { + if (parent.childrenAffectedByFirstChildRules() && elementAfterChange) { // Find our new first child. Element* newFirstElement = ElementTraversal::firstChild(parent); // Find the first element node following |afterChange| // This is the insert/append case. - if (newFirstElement != elementAfterChange && elementAfterChange->renderStyle() && elementAfterChange->renderStyle()->firstChildState()) - elementAfterChange->setNeedsStyleRecalc(); - + if (newFirstElement != elementAfterChange) { + auto* style = elementAfterChange->renderStyle(); + if (!style || style->firstChildState()) + elementAfterChange->invalidateStyleForSubtree(); + } + // We also have to handle node removal. - if (checkType == SiblingElementRemoved && newFirstElement == elementAfterChange && newFirstElement && (!newFirstElement->renderStyle() || !newFirstElement->renderStyle()->firstChildState())) - newFirstElement->setNeedsStyleRecalc(); + if (checkType == SiblingElementRemoved && newFirstElement == elementAfterChange && newFirstElement) { + auto* style = newFirstElement->renderStyle(); + if (!style || !style->firstChildState()) + newFirstElement->invalidateStyleForSubtree(); + } } // :last-child. In the parser callback case, we don't have to check anything, since we were right the first time. // In the DOM case, we only need to do something if |afterChange| is not 0. - if (parent->childrenAffectedByLastChildRules() && elementBeforeChange) { + if (parent.childrenAffectedByLastChildRules() && elementBeforeChange) { // Find our new last child. Element* newLastElement = ElementTraversal::lastChild(parent); - if (newLastElement != elementBeforeChange && elementBeforeChange->renderStyle() && elementBeforeChange->renderStyle()->lastChildState()) - elementBeforeChange->setNeedsStyleRecalc(); - + if (newLastElement != elementBeforeChange) { + auto* style = elementBeforeChange->renderStyle(); + if (!style || style->lastChildState()) + elementBeforeChange->invalidateStyleForSubtree(); + } + // We also have to handle node removal. The parser callback case is similar to node removal as well in that we need to change the last child // to match now. - if ((checkType == SiblingElementRemoved || checkType == FinishedParsingChildren) && newLastElement == elementBeforeChange && newLastElement - && (!newLastElement->renderStyle() || !newLastElement->renderStyle()->lastChildState())) - newLastElement->setNeedsStyleRecalc(); + if ((checkType == SiblingElementRemoved || checkType == FinishedParsingChildren) && newLastElement == elementBeforeChange && newLastElement) { + auto* style = newLastElement->renderStyle(); + if (!style || !style->lastChildState()) + newLastElement->invalidateStyleForSubtree(); + } } - // The + selector. We need to invalidate the first element following the insertion point. It is the only possible element - // that could be affected by this DOM change. - if (parent->childrenAffectedByDirectAdjacentRules() && elementAfterChange) { - elementAfterChange->setNeedsStyleRecalc(); + if (elementAfterChange) { + if (elementAfterChange->styleIsAffectedByPreviousSibling()) + elementAfterChange->invalidateStyleForSubtree(); + else if (elementAfterChange->affectsNextSiblingElementStyle()) { + Element* elementToInvalidate = elementAfterChange; + do { + elementToInvalidate = elementToInvalidate->nextElementSibling(); + } while (elementToInvalidate && !elementToInvalidate->styleIsAffectedByPreviousSibling()); + + if (elementToInvalidate) + elementToInvalidate->invalidateStyleForSubtree(); + } } - // Forward positional selectors include the ~ selector, nth-child, nth-of-type, first-of-type and only-of-type. // Backward positional selectors include nth-last-child, nth-last-of-type, last-of-type and only-of-type. // We have to invalidate everything following the insertion point in the forward case, and everything before the insertion point in the // backward case. // |afterChange| is 0 in the parser callback case, so we won't do any work for the forward case if we don't have to. // For performance reasons we just mark the parent node as changed, since we don't want to make childrenChanged O(n^2) by crawling all our kids // here. recalcStyle will then force a walk of the children when it sees that this has happened. - if (parent->childrenAffectedByForwardPositionalRules() && elementAfterChange) - parent->setNeedsStyleRecalc(); - if (parent->childrenAffectedByBackwardPositionalRules() && elementBeforeChange) - parent->setNeedsStyleRecalc(); + if (parent.childrenAffectedByBackwardPositionalRules() && elementBeforeChange) + parent.invalidateStyleForSubtree(); } void Element::childrenChanged(const ChildChange& change) { ContainerNode::childrenChanged(change); if (change.source == ChildChangeSourceParser) - checkForEmptyStyleChange(this, renderStyle()); + checkForEmptyStyleChange(*this); else { SiblingCheckType checkType = change.type == ElementRemoved ? SiblingElementRemoved : Other; - checkForSiblingStyleChanges(this, checkType, change.previousSiblingElement, change.nextSiblingElement); + checkForSiblingStyleChanges(*this, checkType, change.previousSiblingElement, change.nextSiblingElement); } - if (ShadowRoot* shadowRoot = this->shadowRoot()) - shadowRoot->invalidateDistribution(); + if (ShadowRoot* shadowRoot = this->shadowRoot()) { + switch (change.type) { + case ElementInserted: + case ElementRemoved: + // For elements, we notify shadowRoot in Element::insertedInto and Element::removedFrom. + break; + case AllChildrenRemoved: + case AllChildrenReplaced: + shadowRoot->didRemoveAllChildrenOfShadowHost(); + break; + case TextInserted: + case TextRemoved: + case TextChanged: + shadowRoot->didChangeDefaultSlot(); + break; + case NonContentsChildInserted: + case NonContentsChildRemoved: + break; + } + } +} + +void Element::setAttributeEventListener(const AtomicString& eventType, const QualifiedName& attributeName, const AtomicString& attributeValue) +{ + setAttributeEventListener(eventType, JSLazyEventListener::create(*this, attributeName, attributeValue), mainThreadNormalWorld()); +} + +void Element::setIsNamedFlowContentElement() +{ + ensureElementRareData().setIsNamedFlowContentElement(true); +} + +void Element::clearIsNamedFlowContentElement() +{ + ensureElementRareData().setIsNamedFlowContentElement(false); } void Element::removeAllEventListeners() @@ -1702,20 +2078,16 @@ void Element::removeAllEventListeners() void Element::beginParsingChildren() { clearIsParsingChildrenFinished(); - if (auto styleResolver = document().styleResolverIfExists()) - styleResolver->pushParentElement(this); } void Element::finishParsingChildren() { ContainerNode::finishParsingChildren(); setIsParsingChildrenFinished(); - checkForSiblingStyleChanges(this, FinishedParsingChildren, ElementTraversal::lastChild(this), nullptr); - if (auto styleResolver = document().styleResolverIfExists()) - styleResolver->popParentElement(this); + checkForSiblingStyleChanges(*this, FinishedParsingChildren, ElementTraversal::lastChild(*this), nullptr); } -#ifndef NDEBUG +#if ENABLE(TREE_DEBUGGING) void Element::formatForDebugger(char* buffer, unsigned length) const { StringBuilder result; @@ -1746,103 +2118,149 @@ void Element::formatForDebugger(char* buffer, unsigned length) const const Vector<RefPtr<Attr>>& Element::attrNodeList() { ASSERT(hasSyntheticAttrChildNodes()); - return *attrNodeListForElement(this); + return *attrNodeListForElement(*this); } -PassRefPtr<Attr> Element::setAttributeNode(Attr* attrNode, ExceptionCode& ec) +void Element::attachAttributeNodeIfNeeded(Attr& attrNode) { - if (!attrNode) { - ec = TYPE_MISMATCH_ERR; - return 0; - } + ASSERT(!attrNode.ownerElement() || attrNode.ownerElement() == this); + if (attrNode.ownerElement() == this) + return; + + NoEventDispatchAssertion assertNoEventDispatch; - RefPtr<Attr> oldAttrNode = attrIfExists(attrNode->qualifiedName()); - if (oldAttrNode.get() == attrNode) - return attrNode; // This Attr is already attached to the element. + attrNode.attachToElement(*this); + treeScope().adoptIfNeeded(attrNode); + ensureAttrNodeListForElement(*this).append(&attrNode); +} + +ExceptionOr<RefPtr<Attr>> Element::setAttributeNode(Attr& attrNode) +{ + RefPtr<Attr> oldAttrNode = attrIfExists(attrNode.localName(), shouldIgnoreAttributeCase(*this)); + if (oldAttrNode.get() == &attrNode) + return WTFMove(oldAttrNode); // INUSE_ATTRIBUTE_ERR: Raised if node is an Attr that is already an attribute of another Element object. // The DOM user must explicitly clone Attr nodes to re-use them in other elements. - if (attrNode->ownerElement()) { - ec = INUSE_ATTRIBUTE_ERR; - return 0; - } + if (attrNode.ownerElement() && attrNode.ownerElement() != this) + return Exception { INUSE_ATTRIBUTE_ERR }; + { + NoEventDispatchAssertion assertNoEventDispatch; synchronizeAllAttributes(); - UniqueElementData& elementData = ensureUniqueElementData(); + } - unsigned index = elementData.findAttributeIndexByNameForAttributeNode(attrNode, shouldIgnoreAttributeCase(*this)); - if (index != ElementData::attributeNotFound) { + auto& elementData = ensureUniqueElementData(); + + auto existingAttributeIndex = elementData.findAttributeIndexByName(attrNode.localName(), shouldIgnoreAttributeCase(*this)); + + // Attr::value() will return its 'm_standaloneValue' member any time its Element is set to nullptr. We need to cache this value + // before making changes to attrNode's Element connections. + auto attrNodeValue = attrNode.value(); + + if (existingAttributeIndex == ElementData::attributeNotFound) { + attachAttributeNodeIfNeeded(attrNode); + setAttributeInternal(elementData.findAttributeIndexByName(attrNode.qualifiedName()), attrNode.qualifiedName(), attrNodeValue, NotInSynchronizationOfLazyAttribute); + } else { + const Attribute& attribute = attributeAt(existingAttributeIndex); if (oldAttrNode) - detachAttrNodeFromElementWithValue(oldAttrNode.get(), elementData.attributeAt(index).value()); + detachAttrNodeFromElementWithValue(oldAttrNode.get(), attribute.value()); else - oldAttrNode = Attr::create(document(), attrNode->qualifiedName(), elementData.attributeAt(index).value()); - } + oldAttrNode = Attr::create(document(), attrNode.qualifiedName(), attribute.value()); - setAttributeInternal(index, attrNode->qualifiedName(), attrNode->value(), NotInSynchronizationOfLazyAttribute); + attachAttributeNodeIfNeeded(attrNode); - attrNode->attachToElement(this); - treeScope().adoptIfNeeded(attrNode); - ensureAttrNodeListForElement(this).append(attrNode); + if (attribute.name().matches(attrNode.qualifiedName())) + setAttributeInternal(existingAttributeIndex, attrNode.qualifiedName(), attrNodeValue, NotInSynchronizationOfLazyAttribute); + else { + removeAttributeInternal(existingAttributeIndex, NotInSynchronizationOfLazyAttribute); + setAttributeInternal(ensureUniqueElementData().findAttributeIndexByName(attrNode.qualifiedName()), attrNode.qualifiedName(), attrNodeValue, NotInSynchronizationOfLazyAttribute); + } + } - return oldAttrNode.release(); + return WTFMove(oldAttrNode); } -PassRefPtr<Attr> Element::setAttributeNodeNS(Attr* attr, ExceptionCode& ec) +ExceptionOr<RefPtr<Attr>> Element::setAttributeNodeNS(Attr& attrNode) { - return setAttributeNode(attr, ec); -} + RefPtr<Attr> oldAttrNode = attrIfExists(attrNode.qualifiedName()); + if (oldAttrNode.get() == &attrNode) + return WTFMove(oldAttrNode); -PassRefPtr<Attr> Element::removeAttributeNode(Attr* attr, ExceptionCode& ec) -{ - if (!attr) { - ec = TYPE_MISMATCH_ERR; - return 0; - } - if (attr->ownerElement() != this) { - ec = NOT_FOUND_ERR; - return 0; - } + // INUSE_ATTRIBUTE_ERR: Raised if node is an Attr that is already an attribute of another Element object. + // The DOM user must explicitly clone Attr nodes to re-use them in other elements. + if (attrNode.ownerElement() && attrNode.ownerElement() != this) + return Exception { INUSE_ATTRIBUTE_ERR }; - ASSERT(&document() == &attr->document()); + unsigned index = 0; + + // Attr::value() will return its 'm_standaloneValue' member any time its Element is set to nullptr. We need to cache this value + // before making changes to attrNode's Element connections. + auto attrNodeValue = attrNode.value(); - synchronizeAttribute(attr->qualifiedName()); + { + NoEventDispatchAssertion assertNoEventDispatch; + synchronizeAllAttributes(); + auto& elementData = ensureUniqueElementData(); - unsigned index = elementData()->findAttributeIndexByNameForAttributeNode(attr); - if (index == ElementData::attributeNotFound) { - ec = NOT_FOUND_ERR; - return 0; + index = elementData.findAttributeIndexByName(attrNode.qualifiedName()); + + if (index != ElementData::attributeNotFound) { + if (oldAttrNode) + detachAttrNodeFromElementWithValue(oldAttrNode.get(), elementData.attributeAt(index).value()); + else + oldAttrNode = Attr::create(document(), attrNode.qualifiedName(), elementData.attributeAt(index).value()); + } } - RefPtr<Attr> attrNode = attr; - detachAttrNodeFromElementWithValue(attr, elementData()->attributeAt(index).value()); - removeAttributeInternal(index, NotInSynchronizationOfLazyAttribute); - return attrNode.release(); + attachAttributeNodeIfNeeded(attrNode); + setAttributeInternal(index, attrNode.qualifiedName(), attrNodeValue, NotInSynchronizationOfLazyAttribute); + + return WTFMove(oldAttrNode); } -bool Element::parseAttributeName(QualifiedName& out, const AtomicString& namespaceURI, const AtomicString& qualifiedName, ExceptionCode& ec) +ExceptionOr<Ref<Attr>> Element::removeAttributeNode(Attr& attr) { - String prefix, localName; - if (!Document::parseQualifiedName(qualifiedName, prefix, localName, ec)) - return false; - ASSERT(!ec); + if (attr.ownerElement() != this) + return Exception { NOT_FOUND_ERR }; - QualifiedName qName(prefix, localName, namespaceURI); + ASSERT(&document() == &attr.document()); - if (!Document::hasValidNamespaceForAttributes(qName)) { - ec = NAMESPACE_ERR; - return false; - } + synchronizeAllAttributes(); - out = qName; - return true; + if (!m_elementData) + return Exception { NOT_FOUND_ERR }; + + auto existingAttributeIndex = m_elementData->findAttributeIndexByName(attr.qualifiedName()); + if (existingAttributeIndex == ElementData::attributeNotFound) + return Exception { NOT_FOUND_ERR }; + + Ref<Attr> oldAttrNode { attr }; + + detachAttrNodeFromElementWithValue(&attr, m_elementData->attributeAt(existingAttributeIndex).value()); + removeAttributeInternal(existingAttributeIndex, NotInSynchronizationOfLazyAttribute); + + return WTFMove(oldAttrNode); } -void Element::setAttributeNS(const AtomicString& namespaceURI, const AtomicString& qualifiedName, const AtomicString& value, ExceptionCode& ec) +ExceptionOr<QualifiedName> Element::parseAttributeName(const AtomicString& namespaceURI, const AtomicString& qualifiedName) { - QualifiedName parsedName = anyName; - if (!parseAttributeName(parsedName, namespaceURI, qualifiedName, ec)) - return; - setAttribute(parsedName, value); + auto parseResult = Document::parseQualifiedName(namespaceURI, qualifiedName); + if (parseResult.hasException()) + return parseResult.releaseException(); + QualifiedName parsedAttributeName { parseResult.releaseReturnValue() }; + if (!Document::hasValidNamespaceForAttributes(parsedAttributeName)) + return Exception { NAMESPACE_ERR }; + return WTFMove(parsedAttributeName); +} + +ExceptionOr<void> Element::setAttributeNS(const AtomicString& namespaceURI, const AtomicString& qualifiedName, const AtomicString& value) +{ + auto result = parseAttributeName(namespaceURI, qualifiedName); + if (result.hasException()) + return result.releaseException(); + setAttribute(result.releaseReturnValue(), value); + return { }; } void Element::removeAttributeInternal(unsigned index, SynchronizationOfLazyAttribute inSynchronizationOfLazyAttribute) @@ -1854,62 +2272,74 @@ void Element::removeAttributeInternal(unsigned index, SynchronizationOfLazyAttri QualifiedName name = elementData.attributeAt(index).name(); AtomicString valueBeingRemoved = elementData.attributeAt(index).value(); - if (!inSynchronizationOfLazyAttribute) { - if (!valueBeingRemoved.isNull()) - willModifyAttribute(name, valueBeingRemoved, nullAtom); - } - if (RefPtr<Attr> attrNode = attrIfExists(name)) detachAttrNodeFromElementWithValue(attrNode.get(), elementData.attributeAt(index).value()); - elementData.removeAttribute(index); + if (inSynchronizationOfLazyAttribute) { + elementData.removeAttribute(index); + return; + } + + if (!valueBeingRemoved.isNull()) + willModifyAttribute(name, valueBeingRemoved, nullAtom); - if (!inSynchronizationOfLazyAttribute) - didRemoveAttribute(name, valueBeingRemoved); + { + Style::AttributeChangeInvalidation styleInvalidation(*this, name, valueBeingRemoved, nullAtom); + elementData.removeAttribute(index); + } + + didRemoveAttribute(name, valueBeingRemoved); } void Element::addAttributeInternal(const QualifiedName& name, const AtomicString& value, SynchronizationOfLazyAttribute inSynchronizationOfLazyAttribute) { - if (!inSynchronizationOfLazyAttribute) - willModifyAttribute(name, nullAtom, value); - ensureUniqueElementData().addAttribute(name, value); - if (!inSynchronizationOfLazyAttribute) - didAddAttribute(name, value); + if (inSynchronizationOfLazyAttribute) { + ensureUniqueElementData().addAttribute(name, value); + return; + } + + willModifyAttribute(name, nullAtom, value); + { + Style::AttributeChangeInvalidation styleInvalidation(*this, name, nullAtom, value); + ensureUniqueElementData().addAttribute(name, value); + } + didAddAttribute(name, value); } -void Element::removeAttribute(const AtomicString& name) +bool Element::removeAttribute(const AtomicString& name) { if (!elementData()) - return; + return false; - AtomicString localName = shouldIgnoreAttributeCase(*this) ? name.lower() : name; + AtomicString localName = shouldIgnoreAttributeCase(*this) ? name.convertToASCIILowercase() : name; unsigned index = elementData()->findAttributeIndexByName(localName, false); if (index == ElementData::attributeNotFound) { - if (UNLIKELY(localName == styleAttr) && elementData()->styleAttributeIsDirty() && isStyledElement()) - toStyledElement(this)->removeAllInlineStyleProperties(); - return; + if (UNLIKELY(localName == styleAttr) && elementData()->styleAttributeIsDirty() && is<StyledElement>(*this)) + downcast<StyledElement>(*this).removeAllInlineStyleProperties(); + return false; } removeAttributeInternal(index, NotInSynchronizationOfLazyAttribute); + return true; } -void Element::removeAttributeNS(const AtomicString& namespaceURI, const AtomicString& localName) +bool Element::removeAttributeNS(const AtomicString& namespaceURI, const AtomicString& localName) { - removeAttribute(QualifiedName(nullAtom, localName, namespaceURI)); + return removeAttribute(QualifiedName(nullAtom, localName, namespaceURI)); } -PassRefPtr<Attr> Element::getAttributeNode(const AtomicString& localName) +RefPtr<Attr> Element::getAttributeNode(const AtomicString& localName) { if (!elementData()) - return 0; + return nullptr; synchronizeAttribute(localName); const Attribute* attribute = elementData()->findAttributeByName(localName, shouldIgnoreAttributeCase(*this)); if (!attribute) - return 0; + return nullptr; return ensureAttr(attribute->name()); } -PassRefPtr<Attr> Element::getAttributeNodeNS(const AtomicString& namespaceURI, const AtomicString& localName) +RefPtr<Attr> Element::getAttributeNodeNS(const AtomicString& namespaceURI, const AtomicString& localName) { if (!elementData()) return 0; @@ -1926,7 +2356,7 @@ bool Element::hasAttribute(const AtomicString& localName) const if (!elementData()) return false; synchronizeAttribute(localName); - return elementData()->findAttributeByName(shouldIgnoreAttributeCase(*this) ? localName.lower() : localName, false); + return elementData()->findAttributeByName(localName, shouldIgnoreAttributeCase(*this)); } bool Element::hasAttributeNS(const AtomicString& namespaceURI, const AtomicString& localName) const @@ -1938,18 +2368,22 @@ bool Element::hasAttributeNS(const AtomicString& namespaceURI, const AtomicStrin return elementData()->findAttributeByName(qName); } -CSSStyleDeclaration *Element::style() +CSSStyleDeclaration* Element::cssomStyle() { - return 0; + return nullptr; } void Element::focus(bool restorePreviousSelection, FocusDirection direction) { - if (!inDocument()) + if (!isConnected()) return; - if (document().focusedElement() == this) + if (document().focusedElement() == this) { + if (document().page()) + document().page()->chrome().client().elementDidRefocus(*this); + return; + } // If the stylesheets have already been loaded we can reliably check isFocusable. // If not, we continue and set the focused node on the focus controller below so @@ -1969,7 +2403,7 @@ void Element::focus(bool restorePreviousSelection, FocusDirection direction) // If a focus event handler changes the focus to a different node it // does not make sense to continue and update appearence. protect = this; - if (!page->focusController().setFocusedElement(this, document().frame(), direction)) + if (!page->focusController().setFocusedElement(this, *document().frame(), direction)) return; } @@ -1982,20 +2416,18 @@ void Element::focus(bool restorePreviousSelection, FocusDirection direction) } cancelFocusAppearanceUpdate(); + + SelectionRevealMode revealMode = SelectionRevealMode::Reveal; #if PLATFORM(IOS) // Focusing a form element triggers animation in UIKit to scroll to the right position. // Calling updateFocusAppearance() would generate an unnecessary call to ScrollView::setScrollPosition(), // which would jump us around during this animation. See <rdar://problem/6699741>. - FrameView* view = document().view(); - bool isFormControl = view && isFormControlElement(); + bool isFormControl = is<HTMLFormControlElement>(*this); if (isFormControl) - view->setProhibitsScrolling(true); -#endif - updateFocusAppearance(restorePreviousSelection); -#if PLATFORM(IOS) - if (isFormControl) - view->setProhibitsScrolling(false); + revealMode = SelectionRevealMode::RevealUpToMainFrame; #endif + + updateFocusAppearance(restorePreviousSelection ? SelectionRestorationMode::Restore : SelectionRestorationMode::SetDefault, revealMode); } void Element::updateFocusAppearanceAfterAttachIfNeeded() @@ -2006,76 +2438,167 @@ void Element::updateFocusAppearanceAfterAttachIfNeeded() if (!data->needsFocusAppearanceUpdateSoonAfterAttach()) return; if (isFocusable() && document().focusedElement() == this) - document().updateFocusAppearanceSoon(false /* don't restore selection */); + document().updateFocusAppearanceSoon(SelectionRestorationMode::SetDefault); data->setNeedsFocusAppearanceUpdateSoonAfterAttach(false); } -void Element::updateFocusAppearance(bool /*restorePreviousSelection*/) +void Element::updateFocusAppearance(SelectionRestorationMode, SelectionRevealMode revealMode) { if (isRootEditableElement()) { - Frame* frame = document().frame(); + // Keep frame alive in this method, since setSelection() may release the last reference to |frame|. + RefPtr<Frame> frame = document().frame(); if (!frame) return; // When focusing an editable element in an iframe, don't reset the selection if it already contains a selection. - if (this == frame->selection().rootEditableElement()) + if (this == frame->selection().selection().rootEditableElement()) return; // FIXME: We should restore the previous selection if there is one. VisibleSelection newSelection = VisibleSelection(firstPositionInOrBeforeNode(this), DOWNSTREAM); if (frame->selection().shouldChangeSelection(newSelection)) { - frame->selection().setSelection(newSelection); - frame->selection().revealSelection(); + frame->selection().setSelection(newSelection, FrameSelection::defaultSetSelectionOptions(), Element::defaultFocusTextStateChangeIntent()); + frame->selection().revealSelection(revealMode); } - } else if (renderer() && !renderer()->isWidget()) - renderer()->scrollRectToVisible(boundingBox()); + } else if (renderer() && !renderer()->isWidget()) { + bool insideFixed; + LayoutRect absoluteBounds = renderer()->absoluteAnchorRect(&insideFixed); + renderer()->scrollRectToVisible(revealMode, absoluteBounds, insideFixed); + } } void Element::blur() { cancelFocusAppearanceUpdate(); - if (treeScope().focusedElement() == this) { + if (treeScope().focusedElementInScope() == this) { if (Frame* frame = document().frame()) - frame->page()->focusController().setFocusedElement(0, frame); + frame->page()->focusController().setFocusedElement(nullptr, *frame); else - document().setFocusedElement(0); + document().setFocusedElement(nullptr); } } -void Element::dispatchFocusInEvent(const AtomicString& eventType, PassRefPtr<Element> oldFocusedElement) +void Element::dispatchFocusInEvent(const AtomicString& eventType, RefPtr<Element>&& oldFocusedElement) { - ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden()); + ASSERT_WITH_SECURITY_IMPLICATION(NoEventDispatchAssertion::isEventAllowedInMainThread()); ASSERT(eventType == eventNames().focusinEvent || eventType == eventNames().DOMFocusInEvent); - dispatchScopedEvent(FocusEvent::create(eventType, true, false, document().defaultView(), 0, oldFocusedElement)); + dispatchScopedEvent(FocusEvent::create(eventType, true, false, document().defaultView(), 0, WTFMove(oldFocusedElement))); } -void Element::dispatchFocusOutEvent(const AtomicString& eventType, PassRefPtr<Element> newFocusedElement) +void Element::dispatchFocusOutEvent(const AtomicString& eventType, RefPtr<Element>&& newFocusedElement) { - ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden()); + ASSERT_WITH_SECURITY_IMPLICATION(NoEventDispatchAssertion::isEventAllowedInMainThread()); ASSERT(eventType == eventNames().focusoutEvent || eventType == eventNames().DOMFocusOutEvent); - dispatchScopedEvent(FocusEvent::create(eventType, true, false, document().defaultView(), 0, newFocusedElement)); + dispatchScopedEvent(FocusEvent::create(eventType, true, false, document().defaultView(), 0, WTFMove(newFocusedElement))); } -void Element::dispatchFocusEvent(PassRefPtr<Element> oldFocusedElement, FocusDirection) +void Element::dispatchFocusEvent(RefPtr<Element>&& oldFocusedElement, FocusDirection) { if (document().page()) - document().page()->chrome().client().elementDidFocus(this); + document().page()->chrome().client().elementDidFocus(*this); - RefPtr<FocusEvent> event = FocusEvent::create(eventNames().focusEvent, false, false, document().defaultView(), 0, oldFocusedElement); - EventDispatcher::dispatchEvent(this, event.release()); + EventDispatcher::dispatchEvent(*this, FocusEvent::create(eventNames().focusEvent, false, false, document().defaultView(), 0, WTFMove(oldFocusedElement))); } -void Element::dispatchBlurEvent(PassRefPtr<Element> newFocusedElement) +void Element::dispatchBlurEvent(RefPtr<Element>&& newFocusedElement) { if (document().page()) - document().page()->chrome().client().elementDidBlur(this); + document().page()->chrome().client().elementDidBlur(*this); + + EventDispatcher::dispatchEvent(*this, FocusEvent::create(eventNames().blurEvent, false, false, document().defaultView(), 0, WTFMove(newFocusedElement))); +} + +bool Element::dispatchMouseForceWillBegin() +{ +#if ENABLE(MOUSE_FORCE_EVENTS) + if (!document().hasListenerType(Document::FORCEWILLBEGIN_LISTENER)) + return false; + + Frame* frame = document().frame(); + if (!frame) + return false; + + PlatformMouseEvent platformMouseEvent { frame->eventHandler().lastKnownMousePosition(), frame->eventHandler().lastKnownMouseGlobalPosition(), NoButton, PlatformEvent::NoType, 1, false, false, false, false, WTF::currentTime(), ForceAtClick, NoTap }; + auto mouseForceWillBeginEvent = MouseEvent::create(eventNames().webkitmouseforcewillbeginEvent, document().defaultView(), platformMouseEvent, 0, nullptr); + mouseForceWillBeginEvent->setTarget(this); + dispatchEvent(mouseForceWillBeginEvent); + + if (mouseForceWillBeginEvent->defaultHandled() || mouseForceWillBeginEvent->defaultPrevented()) + return true; +#endif + + return false; +} + +ExceptionOr<void> Element::mergeWithNextTextNode(Text& node) +{ + auto* next = node.nextSibling(); + if (!is<Text>(next)) + return { }; + Ref<Text> textNext { downcast<Text>(*next) }; + node.appendData(textNext->data()); + return textNext->remove(); +} + +String Element::innerHTML() const +{ + return createMarkup(*this, ChildrenOnly); +} + +String Element::outerHTML() const +{ + return createMarkup(*this); +} + +ExceptionOr<void> Element::setOuterHTML(const String& html) +{ + auto* parentElement = this->parentElement(); + if (!is<HTMLElement>(parentElement)) + return Exception { NO_MODIFICATION_ALLOWED_ERR }; - RefPtr<FocusEvent> event = FocusEvent::create(eventNames().blurEvent, false, false, document().defaultView(), 0, newFocusedElement); - EventDispatcher::dispatchEvent(this, event.release()); + Ref<HTMLElement> parent = downcast<HTMLElement>(*parentElement); + RefPtr<Node> prev = previousSibling(); + RefPtr<Node> next = nextSibling(); + + auto fragment = createFragmentForInnerOuterHTML(parent, html, AllowScriptingContent); + if (fragment.hasException()) + return fragment.releaseException(); + + auto replaceResult = parent->replaceChild(fragment.releaseReturnValue().get(), *this); + if (replaceResult.hasException()) + return replaceResult.releaseException(); + + RefPtr<Node> node = next ? next->previousSibling() : nullptr; + if (is<Text>(node.get())) { + auto result = mergeWithNextTextNode(downcast<Text>(*node)); + if (result.hasException()) + return result.releaseException(); + } + if (is<Text>(prev.get())) { + auto result = mergeWithNextTextNode(downcast<Text>(*prev)); + if (result.hasException()) + return result.releaseException(); + } + return { }; } +ExceptionOr<void> Element::setInnerHTML(const String& html) +{ + auto fragment = createFragmentForInnerOuterHTML(*this, html, AllowScriptingContent); + if (fragment.hasException()) + return fragment.releaseException(); + + ContainerNode* container; + if (!is<HTMLTemplateElement>(*this)) + container = this; + else + container = &downcast<HTMLTemplateElement>(*this).content(); + + return replaceChildrenWithFragment(*container, fragment.releaseReturnValue()); +} + String Element::innerText() { // We need to update layout, since plainText uses line boxes in the render tree. @@ -2084,7 +2607,7 @@ String Element::innerText() if (!renderer()) return textContent(true); - return plainText(rangeOfContents(*this).get()); + return plainText(rangeOfContents(*this).ptr()); } String Element::outerText() @@ -2104,12 +2627,12 @@ String Element::title() const const AtomicString& Element::pseudo() const { - return getAttribute(pseudoAttr); + return attributeWithoutSynchronization(pseudoAttr); } void Element::setPseudo(const AtomicString& value) { - setAttribute(pseudoAttr, value); + setAttributeWithoutSynchronization(pseudoAttr, value); } LayoutSize Element::minimumSizeForResizing() const @@ -2124,69 +2647,114 @@ void Element::setMinimumSizeForResizing(const LayoutSize& size) ensureElementRareData().setMinimumSizeForResizing(size); } -static PseudoElement* beforeOrAfterPseudoElement(Element* host, PseudoId pseudoElementSpecifier) +void Element::willBecomeFullscreenElement() +{ + for (auto& child : descendantsOfType<Element>(*this)) + child.ancestorWillEnterFullscreen(); +} + +static PseudoElement* beforeOrAfterPseudoElement(Element& host, PseudoId pseudoElementSpecifier) { switch (pseudoElementSpecifier) { case BEFORE: - return host->beforePseudoElement(); + return host.beforePseudoElement(); case AFTER: - return host->afterPseudoElement(); + return host.afterPseudoElement(); default: - return 0; + return nullptr; } } -RenderStyle* Element::computedStyle(PseudoId pseudoElementSpecifier) +const RenderStyle* Element::existingComputedStyle() { - if (PseudoElement* pseudoElement = beforeOrAfterPseudoElement(this, pseudoElementSpecifier)) - return pseudoElement->computedStyle(); + if (auto* renderTreeStyle = renderStyle()) + return renderTreeStyle; - // FIXME: Find and use the renderer from the pseudo element instead of the actual element so that the 'length' - // properties, which are only known by the renderer because it did the layout, will be correct and so that the - // values returned for the ":selection" pseudo-element will be correct. - if (RenderStyle* usedStyle = renderStyle()) { - if (pseudoElementSpecifier) { - RenderStyle* cachedPseudoStyle = usedStyle->getCachedPseudoStyle(pseudoElementSpecifier); - return cachedPseudoStyle ? cachedPseudoStyle : usedStyle; + if (hasRareData()) + return elementRareData()->computedStyle(); + + return nullptr; +} + +const RenderStyle& Element::resolveComputedStyle() +{ + ASSERT(isConnected()); + ASSERT(!existingComputedStyle()); + + Deque<Element*, 32> elementsRequiringComputedStyle({ this }); + const RenderStyle* computedStyle = nullptr; + + // Collect ancestors until we find one that has style. + auto composedAncestors = composedTreeAncestors(*this); + for (auto& ancestor : composedAncestors) { + elementsRequiringComputedStyle.prepend(&ancestor); + if (auto* existingStyle = ancestor.existingComputedStyle()) { + computedStyle = existingStyle; + break; } - return usedStyle; } - if (!inDocument()) { - // FIXME: Try to do better than this. Ensure that styleForElement() works for elements that are not in the - // document tree and figure out when to destroy the computed style for such elements. + // Resolve and cache styles starting from the most distant ancestor. + for (auto* element : elementsRequiringComputedStyle) { + auto style = document().styleForElementIgnoringPendingStylesheets(*element, computedStyle); + computedStyle = style.get(); + ElementRareData& rareData = element->ensureElementRareData(); + rareData.setComputedStyle(WTFMove(style)); + } + + return *computedStyle; +} + +const RenderStyle* Element::computedStyle(PseudoId pseudoElementSpecifier) +{ + if (!isConnected()) return nullptr; + + if (PseudoElement* pseudoElement = beforeOrAfterPseudoElement(*this, pseudoElementSpecifier)) + return pseudoElement->computedStyle(); + + auto* style = existingComputedStyle(); + if (!style) + style = &resolveComputedStyle(); + + if (pseudoElementSpecifier) { + if (auto* cachedPseudoStyle = style->getCachedPseudoStyle(pseudoElementSpecifier)) + return cachedPseudoStyle; } - ElementRareData& data = ensureElementRareData(); - if (!data.computedStyle()) - data.setComputedStyle(document().styleForElementIgnoringPendingStylesheets(this)); - return pseudoElementSpecifier ? data.computedStyle()->getCachedPseudoStyle(pseudoElementSpecifier) : data.computedStyle(); + return style; } -void Element::setStyleAffectedByEmpty() +bool Element::needsStyleInvalidation() const { - ensureElementRareData().setStyleAffectedByEmpty(true); + if (!inRenderedDocument()) + return false; + if (styleValidity() >= Style::Validity::SubtreeInvalid) + return false; + if (document().hasPendingForcedStyleRecalc()) + return false; + + return true; } -void Element::setChildrenAffectedByActive() +void Element::setStyleAffectedByEmpty() { - ensureElementRareData().setChildrenAffectedByActive(true); + ensureElementRareData().setStyleAffectedByEmpty(true); } -void Element::setChildrenAffectedByDrag() +void Element::setStyleAffectedByFocusWithin() { - ensureElementRareData().setChildrenAffectedByDrag(true); + ensureElementRareData().setStyleAffectedByFocusWithin(true); } -void Element::setChildrenAffectedByDirectAdjacentRules(Element* element) +void Element::setStyleAffectedByActive() { - element->setChildrenAffectedByDirectAdjacentRules(); + ensureElementRareData().setStyleAffectedByActive(true); } -void Element::setChildrenAffectedByForwardPositionalRules(Element* element) +void Element::setChildrenAffectedByDrag() { - element->ensureElementRareData().setChildrenAffectedByForwardPositionalRules(true); + ensureElementRareData().setChildrenAffectedByDrag(true); } void Element::setChildrenAffectedByBackwardPositionalRules() @@ -2194,25 +2762,28 @@ void Element::setChildrenAffectedByBackwardPositionalRules() ensureElementRareData().setChildrenAffectedByBackwardPositionalRules(true); } +void Element::setChildrenAffectedByPropertyBasedBackwardPositionalRules() +{ + ensureElementRareData().setChildrenAffectedByPropertyBasedBackwardPositionalRules(true); +} + void Element::setChildIndex(unsigned index) { ElementRareData& rareData = ensureElementRareData(); - if (RenderStyle* style = renderStyle()) - style->setUnique(); rareData.setChildIndex(index); } bool Element::hasFlagsSetDuringStylingOfChildren() const { - if (childrenAffectedByHover() || childrenAffectedByFirstChildRules() || childrenAffectedByLastChildRules() || childrenAffectedByDirectAdjacentRules()) + if (childrenAffectedByHover() || childrenAffectedByFirstChildRules() || childrenAffectedByLastChildRules()) return true; if (!hasRareData()) return false; - return rareDataChildrenAffectedByActive() + return rareDataStyleAffectedByActive() || rareDataChildrenAffectedByDrag() - || rareDataChildrenAffectedByForwardPositionalRules() - || rareDataChildrenAffectedByBackwardPositionalRules(); + || rareDataChildrenAffectedByBackwardPositionalRules() + || rareDataChildrenAffectedByPropertyBasedBackwardPositionalRules(); } bool Element::rareDataStyleAffectedByEmpty() const @@ -2221,57 +2792,46 @@ bool Element::rareDataStyleAffectedByEmpty() const return elementRareData()->styleAffectedByEmpty(); } -bool Element::rareDataChildrenAffectedByActive() const +bool Element::rareDataStyleAffectedByFocusWithin() const { ASSERT(hasRareData()); - return elementRareData()->childrenAffectedByActive(); + return elementRareData()->styleAffectedByFocusWithin(); } -bool Element::rareDataChildrenAffectedByDrag() const +bool Element::rareDataIsNamedFlowContentElement() const { ASSERT(hasRareData()); - return elementRareData()->childrenAffectedByDrag(); + return elementRareData()->isNamedFlowContentElement(); } -bool Element::rareDataChildrenAffectedByForwardPositionalRules() const +bool Element::rareDataStyleAffectedByActive() const { ASSERT(hasRareData()); - return elementRareData()->childrenAffectedByForwardPositionalRules(); + return elementRareData()->styleAffectedByActive(); } -bool Element::rareDataChildrenAffectedByBackwardPositionalRules() const +bool Element::rareDataChildrenAffectedByDrag() const { ASSERT(hasRareData()); - return elementRareData()->childrenAffectedByBackwardPositionalRules(); + return elementRareData()->childrenAffectedByDrag(); } -unsigned Element::rareDataChildIndex() const +bool Element::rareDataChildrenAffectedByBackwardPositionalRules() const { ASSERT(hasRareData()); - return elementRareData()->childIndex(); -} - -void Element::setIsInCanvasSubtree(bool isInCanvasSubtree) -{ - ensureElementRareData().setIsInCanvasSubtree(isInCanvasSubtree); -} - -bool Element::isInCanvasSubtree() const -{ - return hasRareData() && elementRareData()->isInCanvasSubtree(); + return elementRareData()->childrenAffectedByBackwardPositionalRules(); } -void Element::setIsInsideRegion(bool value) +bool Element::rareDataChildrenAffectedByPropertyBasedBackwardPositionalRules() const { - if (value == isInsideRegion()) - return; - - ensureElementRareData().setIsInsideRegion(value); + ASSERT(hasRareData()); + return elementRareData()->childrenAffectedByPropertyBasedBackwardPositionalRules(); } -bool Element::isInsideRegion() const +unsigned Element::rareDataChildIndex() const { - return hasRareData() ? elementRareData()->isInsideRegion() : false; + ASSERT(hasRareData()); + return elementRareData()->childIndex(); } void Element::setRegionOversetState(RegionOversetState state) @@ -2286,27 +2846,26 @@ RegionOversetState Element::regionOversetState() const AtomicString Element::computeInheritedLanguage() const { - const Node* n = this; - AtomicString value; + if (const ElementData* elementData = this->elementData()) { + if (const Attribute* attribute = elementData->findLanguageAttribute()) + return attribute->value(); + } + // The language property is inherited, so we iterate over the parents to find the first language. - do { - if (n->isElementNode()) { - if (const ElementData* elementData = toElement(n)->elementData()) { - // Spec: xml:lang takes precedence -- http://www.w3.org/TR/xhtml1/#C_7 - if (const Attribute* attribute = elementData->findAttributeByName(XMLNames::langAttr)) - value = attribute->value(); - else if (const Attribute* attribute = elementData->findAttributeByName(HTMLNames::langAttr)) - value = attribute->value(); + const Node* currentNode = this; + while ((currentNode = currentNode->parentNode())) { + if (is<Element>(*currentNode)) { + if (const ElementData* elementData = downcast<Element>(*currentNode).elementData()) { + if (const Attribute* attribute = elementData->findLanguageAttribute()) + return attribute->value(); } - } else if (n->isDocumentNode()) { + } else if (is<Document>(*currentNode)) { // checking the MIME content-language - value = toDocument(n)->contentLanguage(); + return downcast<Document>(*currentNode).contentLanguage(); } + } - n = n->parentNode(); - } while (n && value.isNull()); - - return value; + return nullAtom; } Locale& Element::locale() const @@ -2327,7 +2886,7 @@ void Element::normalizeAttributes() if (!hasAttributes()) return; - auto* attrNodeList = attrNodeListForElement(this); + auto* attrNodeList = attrNodeListForElement(*this); if (!attrNodeList) return; @@ -2341,24 +2900,22 @@ void Element::normalizeAttributes() PseudoElement* Element::beforePseudoElement() const { - return hasRareData() ? elementRareData()->beforePseudoElement() : 0; + return hasRareData() ? elementRareData()->beforePseudoElement() : nullptr; } PseudoElement* Element::afterPseudoElement() const { - return hasRareData() ? elementRareData()->afterPseudoElement() : 0; + return hasRareData() ? elementRareData()->afterPseudoElement() : nullptr; } -void Element::setBeforePseudoElement(PassRefPtr<PseudoElement> element) +void Element::setBeforePseudoElement(Ref<PseudoElement>&& element) { - ensureElementRareData().setBeforePseudoElement(element); - resetNeedsNodeRenderingTraversalSlowPath(); + ensureElementRareData().setBeforePseudoElement(WTFMove(element)); } -void Element::setAfterPseudoElement(PassRefPtr<PseudoElement> element) +void Element::setAfterPseudoElement(Ref<PseudoElement>&& element) { - ensureElementRareData().setAfterPseudoElement(element); - resetNeedsNodeRenderingTraversalSlowPath(); + ensureElementRareData().setAfterPseudoElement(WTFMove(element)); } static void disconnectPseudoElement(PseudoElement* pseudoElement) @@ -2366,7 +2923,7 @@ static void disconnectPseudoElement(PseudoElement* pseudoElement) if (!pseudoElement) return; if (pseudoElement->renderer()) - Style::detachRenderTree(*pseudoElement); + RenderTreeUpdater::tearDownRenderers(*pseudoElement); ASSERT(pseudoElement->hostElement()); pseudoElement->clearHostElement(); } @@ -2377,7 +2934,6 @@ void Element::clearBeforePseudoElement() return; disconnectPseudoElement(elementRareData()->beforePseudoElement()); elementRareData()->setBeforePseudoElement(nullptr); - resetNeedsNodeRenderingTraversalSlowPath(); } void Element::clearAfterPseudoElement() @@ -2386,60 +2942,47 @@ void Element::clearAfterPseudoElement() return; disconnectPseudoElement(elementRareData()->afterPseudoElement()); elementRareData()->setAfterPseudoElement(nullptr); - resetNeedsNodeRenderingTraversalSlowPath(); } -// ElementTraversal API -Element* Element::firstElementChild() const +bool Element::matchesValidPseudoClass() const { - return ElementTraversal::firstChild(this); -} - -Element* Element::lastElementChild() const -{ - return ElementTraversal::lastChild(this); + return false; } -Element* Element::previousElementSibling() const +bool Element::matchesInvalidPseudoClass() const { - return ElementTraversal::previousSibling(this); + return false; } -Element* Element::nextElementSibling() const +bool Element::matchesReadWritePseudoClass() const { - return ElementTraversal::nextSibling(this); + return false; } -unsigned Element::childElementCount() const +bool Element::matchesIndeterminatePseudoClass() const { - unsigned count = 0; - Node* n = firstChild(); - while (n) { - count += n->isElementNode(); - n = n->nextSibling(); - } - return count; + return shouldAppearIndeterminate(); } -bool Element::matchesReadOnlyPseudoClass() const +bool Element::matchesDefaultPseudoClass() const { return false; } -bool Element::matchesReadWritePseudoClass() const +ExceptionOr<bool> Element::matches(const String& selector) { - return false; + auto query = document().selectorQueryForString(selector); + if (query.hasException()) + return query.releaseException(); + return query.releaseReturnValue().matches(*this); } -bool Element::webkitMatchesSelector(const String& selector, ExceptionCode& ec) +ExceptionOr<Element*> Element::closest(const String& selector) { - if (selector.isEmpty()) { - ec = SYNTAX_ERR; - return false; - } - - SelectorQuery* selectorQuery = document().selectorQueryCache().add(selector, document(), ec); - return selectorQuery && selectorQuery->matches(*this); + auto query = document().selectorQueryForString(selector); + if (query.hasException()) + return query.releaseException(); + return query.releaseReturnValue().closest(*this); } bool Element::shouldAppearIndeterminate() const @@ -2447,20 +2990,25 @@ bool Element::shouldAppearIndeterminate() const return false; } -DOMTokenList* Element::classList() +bool Element::mayCauseRepaintInsideViewport(const IntRect* visibleRect) const +{ + return renderer() && renderer()->mayCauseRepaintInsideViewport(visibleRect); +} + +DOMTokenList& Element::classList() { ElementRareData& data = ensureElementRareData(); if (!data.classList()) - data.setClassList(std::make_unique<ClassList>(*this)); - return data.classList(); + data.setClassList(std::make_unique<DOMTokenList>(*this, HTMLNames::classAttr)); + return *data.classList(); } -DatasetDOMStringMap* Element::dataset() +DatasetDOMStringMap& Element::dataset() { ElementRareData& data = ensureElementRareData(); if (!data.dataset()) data.setDataset(std::make_unique<DatasetDOMStringMap>(*this)); - return data.dataset(); + return *data.dataset(); } URL Element::getURLAttribute(const QualifiedName& name) const @@ -2490,7 +3038,7 @@ URL Element::getNonEmptyURLAttribute(const QualifiedName& name) const int Element::getIntegralAttribute(const QualifiedName& attributeName) const { - return getAttribute(attributeName).string().toInt(); + return parseHTMLInteger(getAttribute(attributeName)).value_or(0); } void Element::setIntegralAttribute(const QualifiedName& attributeName, int value) @@ -2500,47 +3048,29 @@ void Element::setIntegralAttribute(const QualifiedName& attributeName, int value unsigned Element::getUnsignedIntegralAttribute(const QualifiedName& attributeName) const { - return getAttribute(attributeName).string().toUInt(); + return parseHTMLNonNegativeInteger(getAttribute(attributeName)).value_or(0); } void Element::setUnsignedIntegralAttribute(const QualifiedName& attributeName, unsigned value) { - setAttribute(attributeName, AtomicString::number(value)); + setAttribute(attributeName, AtomicString::number(limitToOnlyHTMLNonNegative(value))); } -#if ENABLE(INDIE_UI) -void Element::setUIActions(const AtomicString& actions) -{ - setAttribute(uiactionsAttr, actions); -} - -const AtomicString& Element::UIActions() const -{ - return getAttribute(uiactionsAttr); -} -#endif - bool Element::childShouldCreateRenderer(const Node& child) const { -#if ENABLE(SVG) // Only create renderers for SVG elements whose parents are SVG elements, or for proper <svg xmlns="svgNS"> subdocuments. if (child.isSVGElement()) { ASSERT(!isSVGElement()); - return child.hasTagName(SVGNames::svgTag) && toSVGElement(child).isValid(); + const SVGElement& childElement = downcast<SVGElement>(child); + return is<SVGSVGElement>(childElement) && childElement.isValid(); } -#endif - return ContainerNode::childShouldCreateRenderer(child); + return true; } #if ENABLE(FULLSCREEN_API) void Element::webkitRequestFullscreen() { - document().requestFullScreenForElement(this, ALLOW_KEYBOARD_INPUT, Document::EnforceIFrameAllowFullScreenRequirement); -} - -void Element::webkitRequestFullScreen(unsigned short flags) -{ - document().requestFullScreenForElement(this, (flags | LEGACY_MOZILLA_REQUEST), Document::EnforceIFrameAllowFullScreenRequirement); + document().requestFullScreenForElement(this, Document::EnforceIFrameAllowFullScreenRequirement); } bool Element::containsFullScreenElement() const @@ -2551,7 +3081,7 @@ bool Element::containsFullScreenElement() const void Element::setContainsFullScreenElement(bool flag) { ensureElementRareData().setContainsFullScreenElement(flag); - setNeedsStyleRecalc(SyntheticStyleChange); + invalidateStyleAndLayerComposition(); } static Element* parentCrossingFrameBoundaries(Element* element) @@ -2569,23 +3099,22 @@ void Element::setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(boo #endif #if ENABLE(POINTER_LOCK) -void Element::webkitRequestPointerLock() +void Element::requestPointerLock() { if (document().page()) - document().page()->pointerLockController()->requestPointerLock(this); + document().page()->pointerLockController().requestPointerLock(this); } #endif SpellcheckAttributeState Element::spellcheckAttributeState() const { - const AtomicString& value = getAttribute(HTMLNames::spellcheckAttr); - if (value == nullAtom) + const AtomicString& value = attributeWithoutSynchronization(HTMLNames::spellcheckAttr); + if (value.isNull()) return SpellcheckAttributeDefault; - if (equalIgnoringCase(value, "true") || equalIgnoringCase(value, "")) + if (value.isEmpty() || equalLettersIgnoringASCIICase(value, "true")) return SpellcheckAttributeTrue; - if (equalIgnoringCase(value, "false")) + if (equalLettersIgnoringASCIICase(value, "false")) return SpellcheckAttributeFalse; - return SpellcheckAttributeDefault; } @@ -2605,12 +3134,12 @@ bool Element::isSpellCheckingEnabled() const return true; } -RenderRegion* Element::renderRegion() const +RenderNamedFlowFragment* Element::renderNamedFlowFragment() const { if (renderer() && renderer()->isRenderNamedFlowFragmentContainer()) - return toRenderBlockFlow(renderer())->renderNamedFlowFragment(); + return downcast<RenderBlockFlow>(*renderer()).renderNamedFlowFragment(); - return 0; + return nullptr; } #if ENABLE(CSS_REGIONS) @@ -2625,31 +3154,31 @@ bool Element::shouldMoveToFlowThread(const RenderStyle& styleToUse) const if (isInShadowTree()) return false; - if (styleToUse.flowThread().isEmpty()) + if (!styleToUse.hasFlowInto()) return false; - return !document().renderView()->flowThreadController().isContentElementRegisteredWithAnyNamedFlow(*this); + return true; } const AtomicString& Element::webkitRegionOverset() const { document().updateLayoutIgnorePendingStylesheets(); - DEFINE_STATIC_LOCAL(AtomicString, undefinedState, ("undefined", AtomicString::ConstructFromLiteral)); - if (!document().cssRegionsEnabled() || !renderRegion()) + static NeverDestroyed<AtomicString> undefinedState("undefined", AtomicString::ConstructFromLiteral); + if (!renderNamedFlowFragment()) return undefinedState; - switch (renderRegion()->regionOversetState()) { + switch (regionOversetState()) { case RegionFit: { - DEFINE_STATIC_LOCAL(AtomicString, fitState, ("fit", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<AtomicString> fitState("fit", AtomicString::ConstructFromLiteral); return fitState; } case RegionEmpty: { - DEFINE_STATIC_LOCAL(AtomicString, emptyState, ("empty", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<AtomicString> emptyState("empty", AtomicString::ConstructFromLiteral); return emptyState; } case RegionOverset: { - DEFINE_STATIC_LOCAL(AtomicString, overflowState, ("overset", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<AtomicString> overflowState("overset", AtomicString::ConstructFromLiteral); return overflowState; } case RegionUndefined: @@ -2663,16 +3192,13 @@ const AtomicString& Element::webkitRegionOverset() const Vector<RefPtr<Range>> Element::webkitGetRegionFlowRanges() const { Vector<RefPtr<Range>> rangeObjects; - if (!document().cssRegionsEnabled()) - return rangeObjects; - document().updateLayoutIgnorePendingStylesheets(); - if (renderer() && renderer()->isRenderNamedFlowFragmentContainer()) { - RenderNamedFlowFragment* region = toRenderBlockFlow(renderer())->renderNamedFlowFragment(); - if (region->isValid()) - region->getRanges(rangeObjects); + auto* renderer = this->renderer(); + if (renderer && renderer->isRenderNamedFlowFragmentContainer()) { + auto& namedFlowFragment = *downcast<RenderBlockFlow>(*renderer).renderNamedFlowFragment(); + if (namedFlowFragment.isValid()) + namedFlowFragment.getRanges(rangeObjects); } - return rangeObjects; } @@ -2684,10 +3210,8 @@ bool Element::fastAttributeLookupAllowed(const QualifiedName& name) const if (name == HTMLNames::styleAttr) return false; -#if ENABLE(SVG) if (isSVGElement()) - return !toSVGElement(this)->isAnimatableAttribute(name); -#endif + return !downcast<SVGElement>(*this).isAnimatableAttribute(name); return true; } @@ -2710,11 +3234,11 @@ inline void Element::updateName(const AtomicString& oldName, const AtomicString& updateNameForTreeScope(treeScope(), oldName, newName); - if (!inDocument()) + if (!isConnected()) return; - if (!document().isHTMLDocument()) + if (!is<HTMLDocument>(document())) return; - updateNameForDocument(toHTMLDocument(document()), oldName, newName); + updateNameForDocument(downcast<HTMLDocument>(document()), oldName, newName); } void Element::updateNameForTreeScope(TreeScope& scope, const AtomicString& oldName, const AtomicString& newName) @@ -2731,16 +3255,19 @@ void Element::updateNameForDocument(HTMLDocument& document, const AtomicString& { ASSERT(oldName != newName); - if (WindowNameCollection::nodeMatchesIfNameAttributeMatch(this)) { - const AtomicString& id = WindowNameCollection::nodeMatchesIfIdAttributeMatch(this) ? getIdAttribute() : nullAtom; + if (isInShadowTree()) + return; + + if (WindowNameCollection::elementMatchesIfNameAttributeMatch(*this)) { + const AtomicString& id = WindowNameCollection::elementMatchesIfIdAttributeMatch(*this) ? getIdAttribute() : nullAtom; if (!oldName.isEmpty() && oldName != id) document.removeWindowNamedItem(*oldName.impl(), *this); if (!newName.isEmpty() && newName != id) document.addWindowNamedItem(*newName.impl(), *this); } - if (DocumentNameCollection::nodeMatchesIfNameAttributeMatch(this)) { - const AtomicString& id = DocumentNameCollection::nodeMatchesIfIdAttributeMatch(this) ? getIdAttribute() : nullAtom; + if (DocumentNameCollection::elementMatchesIfNameAttributeMatch(*this)) { + const AtomicString& id = DocumentNameCollection::elementMatchesIfIdAttributeMatch(*this) ? getIdAttribute() : nullAtom; if (!oldName.isEmpty() && oldName != id) document.removeDocumentNamedItem(*oldName.impl(), *this); if (!newName.isEmpty() && newName != id) @@ -2758,11 +3285,11 @@ inline void Element::updateId(const AtomicString& oldId, const AtomicString& new updateIdForTreeScope(treeScope(), oldId, newId, notifyObservers); - if (!inDocument()) + if (!isConnected()) return; - if (!document().isHTMLDocument()) + if (!is<HTMLDocument>(document())) return; - updateIdForDocument(toHTMLDocument(document()), oldId, newId, UpdateHTMLDocumentNamedItemMapsOnlyIfDiffersFromNameAttribute); + updateIdForDocument(downcast<HTMLDocument>(document()), oldId, newId, UpdateHTMLDocumentNamedItemMapsOnlyIfDiffersFromNameAttribute); } void Element::updateIdForTreeScope(TreeScope& scope, const AtomicString& oldId, const AtomicString& newId, NotifyObservers notifyObservers) @@ -2778,19 +3305,22 @@ void Element::updateIdForTreeScope(TreeScope& scope, const AtomicString& oldId, void Element::updateIdForDocument(HTMLDocument& document, const AtomicString& oldId, const AtomicString& newId, HTMLDocumentNamedItemMapsUpdatingCondition condition) { - ASSERT(inDocument()); + ASSERT(isConnected()); ASSERT(oldId != newId); - if (WindowNameCollection::nodeMatchesIfIdAttributeMatch(this)) { - const AtomicString& name = condition == UpdateHTMLDocumentNamedItemMapsOnlyIfDiffersFromNameAttribute && WindowNameCollection::nodeMatchesIfNameAttributeMatch(this) ? getNameAttribute() : nullAtom; + if (isInShadowTree()) + return; + + if (WindowNameCollection::elementMatchesIfIdAttributeMatch(*this)) { + const AtomicString& name = condition == UpdateHTMLDocumentNamedItemMapsOnlyIfDiffersFromNameAttribute && WindowNameCollection::elementMatchesIfNameAttributeMatch(*this) ? getNameAttribute() : nullAtom; if (!oldId.isEmpty() && oldId != name) document.removeWindowNamedItem(*oldId.impl(), *this); if (!newId.isEmpty() && newId != name) document.addWindowNamedItem(*newId.impl(), *this); } - if (DocumentNameCollection::nodeMatchesIfIdAttributeMatch(this)) { - const AtomicString& name = condition == UpdateHTMLDocumentNamedItemMapsOnlyIfDiffersFromNameAttribute && DocumentNameCollection::nodeMatchesIfNameAttributeMatch(this) ? getNameAttribute() : nullAtom; + if (DocumentNameCollection::elementMatchesIfIdAttributeMatch(*this)) { + const AtomicString& name = condition == UpdateHTMLDocumentNamedItemMapsOnlyIfDiffersFromNameAttribute && DocumentNameCollection::elementMatchesIfNameAttributeMatch(*this) ? getNameAttribute() : nullAtom; if (!oldId.isEmpty() && oldId != name) document.removeDocumentNamedItem(*oldId.impl(), *this); if (!newId.isEmpty() && newId != name) @@ -2802,21 +3332,21 @@ void Element::updateLabel(TreeScope& scope, const AtomicString& oldForAttributeV { ASSERT(hasTagName(labelTag)); - if (!inDocument()) + if (!isConnected()) return; if (oldForAttributeValue == newForAttributeValue) return; if (!oldForAttributeValue.isEmpty()) - scope.removeLabel(*oldForAttributeValue.impl(), *toHTMLLabelElement(this)); + scope.removeLabel(*oldForAttributeValue.impl(), downcast<HTMLLabelElement>(*this)); if (!newForAttributeValue.isEmpty()) - scope.addLabel(*newForAttributeValue.impl(), *toHTMLLabelElement(this)); + scope.addLabel(*newForAttributeValue.impl(), downcast<HTMLLabelElement>(*this)); } void Element::willModifyAttribute(const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue) { - if (isIdAttributeName(name)) + if (name == HTMLNames::idAttr) updateId(oldValue, newValue, NotifyObservers::No); // Will notify observers after the attribute is actually changed. else if (name == HTMLNames::nameAttr) updateName(oldValue, newValue); @@ -2825,92 +3355,69 @@ void Element::willModifyAttribute(const QualifiedName& name, const AtomicString& updateLabel(treeScope(), oldValue, newValue); } - if (oldValue != newValue) { - auto styleResolver = document().styleResolverIfExists(); - if (styleResolver && styleResolver->hasSelectorForAttribute(name.localName())) - setNeedsStyleRecalc(); - } - - if (OwnPtr<MutationObserverInterestGroup> recipients = MutationObserverInterestGroup::createForAttributesMutation(*this, name)) + if (auto recipients = MutationObserverInterestGroup::createForAttributesMutation(*this, name)) recipients->enqueueMutationRecord(MutationRecord::createAttributes(*this, name, oldValue)); -#if ENABLE(INSPECTOR) - InspectorInstrumentation::willModifyDOMAttr(&document(), this, oldValue, newValue); -#endif + InspectorInstrumentation::willModifyDOMAttr(document(), *this, oldValue, newValue); } void Element::didAddAttribute(const QualifiedName& name, const AtomicString& value) { attributeChanged(name, nullAtom, value); - InspectorInstrumentation::didModifyDOMAttr(&document(), this, name.localName(), value); + InspectorInstrumentation::didModifyDOMAttr(document(), *this, name.localName(), value); dispatchSubtreeModifiedEvent(); } void Element::didModifyAttribute(const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue) { attributeChanged(name, oldValue, newValue); - InspectorInstrumentation::didModifyDOMAttr(&document(), this, name.localName(), newValue); + InspectorInstrumentation::didModifyDOMAttr(document(), *this, name.localName(), newValue); // Do not dispatch a DOMSubtreeModified event here; see bug 81141. } void Element::didRemoveAttribute(const QualifiedName& name, const AtomicString& oldValue) { attributeChanged(name, oldValue, nullAtom); - InspectorInstrumentation::didRemoveDOMAttr(&document(), this, name.localName()); + InspectorInstrumentation::didRemoveDOMAttr(document(), *this, name.localName()); dispatchSubtreeModifiedEvent(); } -PassRefPtr<HTMLCollection> Element::ensureCachedHTMLCollection(CollectionType type) -{ - if (HTMLCollection* collection = cachedHTMLCollection(type)) - return collection; - - RefPtr<HTMLCollection> collection; - if (type == TableRows) { - return ensureRareData().ensureNodeLists().addCachedCollection<HTMLTableRowsCollection>(toHTMLTableElement(*this), type); - } else if (type == SelectOptions) { - return ensureRareData().ensureNodeLists().addCachedCollection<HTMLOptionsCollection>(toHTMLSelectElement(*this), type); - } else if (type == FormControls) { - ASSERT(hasTagName(formTag) || hasTagName(fieldsetTag)); - return ensureRareData().ensureNodeLists().addCachedCollection<HTMLFormControlsCollection>(*this, type); - } - return ensureRareData().ensureNodeLists().addCachedCollection<HTMLCollection>(*this, type); -} - -HTMLCollection* Element::cachedHTMLCollection(CollectionType type) +IntPoint Element::savedLayerScrollPosition() const { - return hasRareData() && rareData()->nodeLists() ? rareData()->nodeLists()->cachedCollection<HTMLCollection>(type) : 0; + return hasRareData() ? elementRareData()->savedLayerScrollPosition() : IntPoint(); } -IntSize Element::savedLayerScrollOffset() const +void Element::setSavedLayerScrollPosition(const IntPoint& position) { - return hasRareData() ? elementRareData()->savedLayerScrollOffset() : IntSize(); + if (position.isZero() && !hasRareData()) + return; + ensureElementRareData().setSavedLayerScrollPosition(position); } -void Element::setSavedLayerScrollOffset(const IntSize& size) +RefPtr<Attr> Element::attrIfExists(const AtomicString& localName, bool shouldIgnoreAttributeCase) { - if (size.isZero() && !hasRareData()) - return; - ensureElementRareData().setSavedLayerScrollOffset(size); + if (auto* attrNodeList = attrNodeListForElement(*this)) + return findAttrNodeInList(*attrNodeList, localName, shouldIgnoreAttributeCase); + return nullptr; } -PassRefPtr<Attr> Element::attrIfExists(const QualifiedName& name) +RefPtr<Attr> Element::attrIfExists(const QualifiedName& name) { - if (AttrNodeList* attrNodeList = attrNodeListForElement(this)) + if (auto* attrNodeList = attrNodeListForElement(*this)) return findAttrNodeInList(*attrNodeList, name); - return 0; + return nullptr; } -PassRefPtr<Attr> Element::ensureAttr(const QualifiedName& name) +Ref<Attr> Element::ensureAttr(const QualifiedName& name) { - AttrNodeList& attrNodeList = ensureAttrNodeListForElement(this); + auto& attrNodeList = ensureAttrNodeListForElement(*this); RefPtr<Attr> attrNode = findAttrNodeInList(attrNodeList, name); if (!attrNode) { - attrNode = Attr::create(this, name); - treeScope().adoptIfNeeded(attrNode.get()); + attrNode = Attr::create(*this, name); + treeScope().adoptIfNeeded(*attrNode); attrNodeList.append(attrNode); } - return attrNode.release(); + return attrNode.releaseNonNull(); } void Element::detachAttrNodeFromElementWithValue(Attr* attrNode, const AtomicString& value) @@ -2918,21 +3425,18 @@ void Element::detachAttrNodeFromElementWithValue(Attr* attrNode, const AtomicStr ASSERT(hasSyntheticAttrChildNodes()); attrNode->detachFromElementWithValue(value); - AttrNodeList* attrNodeList = attrNodeListForElement(this); - for (unsigned i = 0; i < attrNodeList->size(); ++i) { - if (attrNodeList->at(i)->qualifiedName() == attrNode->qualifiedName()) { - attrNodeList->remove(i); - if (attrNodeList->isEmpty()) - removeAttrNodeListForElement(this); - return; - } - } - ASSERT_NOT_REACHED(); + auto& attrNodeList = *attrNodeListForElement(*this); + bool found = attrNodeList.removeFirstMatching([attrNode](auto& attribute) { + return attribute->qualifiedName() == attrNode->qualifiedName(); + }); + ASSERT_UNUSED(found, found); + if (attrNodeList.isEmpty()) + removeAttrNodeListForElement(*this); } void Element::detachAllAttrNodesFromElement() { - AttrNodeList* attrNodeList = attrNodeListForElement(this); + auto* attrNodeList = attrNodeListForElement(*this); ASSERT(attrNodeList); for (const Attribute& attribute : attributesIterator()) { @@ -2940,18 +3444,31 @@ void Element::detachAllAttrNodesFromElement() attrNode->detachFromElementWithValue(attribute.value()); } - removeAttrNodeListForElement(this); + removeAttrNodeListForElement(*this); } void Element::resetComputedStyle() { if (!hasRareData() || !elementRareData()->computedStyle()) return; - elementRareData()->resetComputedStyle(); - for (auto& child : descendantsOfType<Element>(*this)) { - if (child.hasRareData()) - child.elementRareData()->resetComputedStyle(); - } + + auto reset = [](Element& element) { + if (!element.hasRareData() || !element.elementRareData()->computedStyle()) + return; + if (element.hasCustomStyleResolveCallbacks()) + element.willResetComputedStyle(); + element.elementRareData()->resetComputedStyle(); + }; + reset(*this); + for (auto& child : descendantsOfType<Element>(*this)) + reset(child); +} + +void Element::resetStyleRelations() +{ + if (!hasRareData()) + return; + elementRareData()->resetStyleRelations(); } void Element::clearStyleDerivedDataBeforeDetachingRenderer() @@ -2960,13 +3477,6 @@ void Element::clearStyleDerivedDataBeforeDetachingRenderer() cancelFocusAppearanceUpdate(); clearBeforePseudoElement(); clearAfterPseudoElement(); - if (!hasRareData()) - return; - ElementRareData* data = elementRareData(); - data->setIsInCanvasSubtree(false); - data->resetComputedStyle(); - data->resetDynamicRestyleObservations(); - data->setIsInsideRegion(false); } void Element::clearHoverAndActiveStatusBeforeDetachingRenderer() @@ -2980,10 +3490,9 @@ void Element::clearHoverAndActiveStatusBeforeDetachingRenderer() document().userActionElements().didDetach(this); } -bool Element::willRecalcStyle(Style::Change) +void Element::willRecalcStyle(Style::Change) { ASSERT(hasCustomStyleResolveCallbacks()); - return true; } void Element::didRecalcStyle(Style::Change) @@ -2991,6 +3500,11 @@ void Element::didRecalcStyle(Style::Change) ASSERT(hasCustomStyleResolveCallbacks()); } +void Element::willResetComputedStyle() +{ + ASSERT(hasCustomStyleResolveCallbacks()); +} + void Element::willAttachRenderers() { ASSERT(hasCustomStyleResolveCallbacks()); @@ -3011,10 +3525,10 @@ void Element::didDetachRenderers() ASSERT(hasCustomStyleResolveCallbacks()); } -PassRefPtr<RenderStyle> Element::customStyleForRenderer() +std::optional<ElementStyle> Element::resolveCustomStyle(const RenderStyle&, const RenderStyle*) { ASSERT(hasCustomStyleResolveCallbacks()); - return 0; + return std::nullopt; } void Element::cloneAttributesFromElement(const Element& other) @@ -3024,13 +3538,13 @@ void Element::cloneAttributesFromElement(const Element& other) other.synchronizeAllAttributes(); if (!other.m_elementData) { - m_elementData.clear(); + m_elementData = nullptr; return; } // We can't update window and document's named item maps since the presence of image and object elements depend on other attributes and children. // Fortunately, those named item maps are only updated when this element is in the document, which should never be the case. - ASSERT(!inDocument()); + ASSERT(!isConnected()); const AtomicString& oldID = getIdAttribute(); const AtomicString& newID = other.getIdAttribute(); @@ -3046,10 +3560,10 @@ void Element::cloneAttributesFromElement(const Element& other) // If 'other' has a mutable ElementData, convert it to an immutable one so we can share it between both elements. // We can only do this if there is no CSSOM wrapper for other's inline style, and there are no presentation attributes. - if (other.m_elementData->isUnique() + if (is<UniqueElementData>(*other.m_elementData) && !other.m_elementData->presentationAttributeStyle() && (!other.m_elementData->inlineStyle() || !other.m_elementData->inlineStyle()->hasCSSOMWrapper())) - const_cast<Element&>(other).m_elementData = static_cast<const UniqueElementData*>(other.m_elementData.get())->makeShareableCopy(); + const_cast<Element&>(other).m_elementData = downcast<UniqueElementData>(*other.m_elementData).makeShareableCopy(); if (!other.m_elementData->isUnique()) m_elementData = other.m_elementData; @@ -3070,13 +3584,10 @@ void Element::createUniqueElementData() { if (!m_elementData) m_elementData = UniqueElementData::create(); - else { - ASSERT(!m_elementData->isUnique()); - m_elementData = static_cast<ShareableElementData*>(m_elementData.get())->makeUniqueCopy(); - } + else + m_elementData = downcast<ShareableElementData>(*m_elementData).makeUniqueCopy(); } -#if ENABLE(SVG) bool Element::hasPendingResources() const { return hasRareData() && elementRareData()->hasPendingResources(); @@ -3091,6 +3602,143 @@ void Element::clearHasPendingResources() { ensureElementRareData().setHasPendingResources(false); } -#endif + +bool Element::canContainRangeEndPoint() const +{ + return !equalLettersIgnoringASCIICase(attributeWithoutSynchronization(roleAttr), "img"); +} + +String Element::completeURLsInAttributeValue(const URL& base, const Attribute& attribute) const +{ + return URL(base, attribute.value()).string(); +} + +ExceptionOr<Node*> Element::insertAdjacent(const String& where, Ref<Node>&& newChild) +{ + // In Internet Explorer if the element has no parent and where is "beforeBegin" or "afterEnd", + // a document fragment is created and the elements appended in the correct order. This document + // fragment isn't returned anywhere. + // + // This is impossible for us to implement as the DOM tree does not allow for such structures, + // Opera also appears to disallow such usage. + + if (equalLettersIgnoringASCIICase(where, "beforebegin")) { + auto* parent = this->parentNode(); + if (!parent) + return nullptr; + auto result = parent->insertBefore(newChild, this); + if (result.hasException()) + return result.releaseException(); + return newChild.ptr(); + } + + if (equalLettersIgnoringASCIICase(where, "afterbegin")) { + auto result = insertBefore(newChild, firstChild()); + if (result.hasException()) + return result.releaseException(); + return newChild.ptr(); + } + + if (equalLettersIgnoringASCIICase(where, "beforeend")) { + auto result = appendChild(newChild); + if (result.hasException()) + return result.releaseException(); + return newChild.ptr(); + } + + if (equalLettersIgnoringASCIICase(where, "afterend")) { + auto* parent = this->parentNode(); + if (!parent) + return nullptr; + auto result = parent->insertBefore(newChild, nextSibling()); + if (result.hasException()) + return result.releaseException(); + return newChild.ptr(); + } + + return Exception { SYNTAX_ERR }; +} + +ExceptionOr<Element*> Element::insertAdjacentElement(const String& where, Element& newChild) +{ + auto result = insertAdjacent(where, newChild); + if (result.hasException()) + return result.releaseException(); + return downcast<Element>(result.releaseReturnValue()); +} + +// Step 1 of https://w3c.github.io/DOM-Parsing/#dom-element-insertadjacenthtml. +static ExceptionOr<ContainerNode&> contextNodeForInsertion(const String& where, Element& element) +{ + if (equalLettersIgnoringASCIICase(where, "beforebegin") || equalLettersIgnoringASCIICase(where, "afterend")) { + auto* parent = element.parentNode(); + if (!parent || is<Document>(*parent)) + return Exception { NO_MODIFICATION_ALLOWED_ERR }; + return *parent; + } + if (equalLettersIgnoringASCIICase(where, "afterbegin") || equalLettersIgnoringASCIICase(where, "beforeend")) + return element; + return Exception { SYNTAX_ERR }; +} + +// Step 2 of https://w3c.github.io/DOM-Parsing/#dom-element-insertadjacenthtml. +static ExceptionOr<Ref<Element>> contextElementForInsertion(const String& where, Element& element) +{ + auto contextNodeResult = contextNodeForInsertion(where, element); + if (contextNodeResult.hasException()) + return contextNodeResult.releaseException(); + auto& contextNode = contextNodeResult.releaseReturnValue(); + if (!is<Element>(contextNode) || (contextNode.document().isHTMLDocument() && is<HTMLHtmlElement>(contextNode))) + return Ref<Element> { HTMLBodyElement::create(contextNode.document()) }; + return Ref<Element> { downcast<Element>(contextNode) }; +} + +// https://w3c.github.io/DOM-Parsing/#dom-element-insertadjacenthtml +ExceptionOr<void> Element::insertAdjacentHTML(const String& where, const String& markup) +{ + // Steps 1 and 2. + auto contextElement = contextElementForInsertion(where, *this); + if (contextElement.hasException()) + return contextElement.releaseException(); + // Step 3. + auto fragment = createFragmentForInnerOuterHTML(contextElement.releaseReturnValue(), markup, AllowScriptingContent); + if (fragment.hasException()) + return fragment.releaseException(); + // Step 4. + auto result = insertAdjacent(where, fragment.releaseReturnValue()); + if (result.hasException()) + return result.releaseException(); + return { }; +} + +ExceptionOr<void> Element::insertAdjacentText(const String& where, const String& text) +{ + auto result = insertAdjacent(where, document().createTextNode(text)); + if (result.hasException()) + return result.releaseException(); + return { }; +} + +Element* Element::findAnchorElementForLink(String& outAnchorName) +{ + if (!isLink()) + return nullptr; + + const AtomicString& href = attributeWithoutSynchronization(HTMLNames::hrefAttr); + if (href.isNull()) + return nullptr; + + Document& document = this->document(); + URL url = document.completeURL(href); + if (!url.isValid()) + return nullptr; + + if (url.hasFragmentIdentifier() && equalIgnoringFragmentIdentifier(url, document.baseURL())) { + outAnchorName = url.fragmentIdentifier(); + return document.findAnchor(outAnchorName); + } + + return nullptr; +} } // namespace WebCore diff --git a/Source/WebCore/dom/Element.h b/Source/WebCore/dom/Element.h index 3d2741a93..089cc1041 100644 --- a/Source/WebCore/dom/Element.h +++ b/Source/WebCore/dom/Element.h @@ -3,7 +3,7 @@ * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Peter Kelly (pmk@post.com) * (C) 2001 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2003-2017 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -22,44 +22,40 @@ * */ -#ifndef Element_h -#define Element_h +#pragma once -#include "CollectionType.h" +#include "AXTextStateChangeIntent.h" #include "Document.h" #include "ElementData.h" #include "HTMLNames.h" #include "RegionOversetState.h" +#include "ScrollToOptions.h" #include "ScrollTypes.h" -#include "StyleResolveTree.h" +#include "ShadowRootMode.h" +#include "SimulatedClickOptions.h" +#include "StyleChange.h" namespace WebCore { class ClientRect; class ClientRectList; +class CustomElementReactionQueue; class DatasetDOMStringMap; class DOMTokenList; class ElementRareData; class HTMLDocument; class IntSize; +class JSCustomElementInterface; +class KeyboardEvent; class Locale; class PlatformKeyboardEvent; class PlatformMouseEvent; class PlatformWheelEvent; class PseudoElement; -class RenderRegion; -class ShadowRoot; - -enum AffectedSelectorType { - AffectedSelectorChecked = 1, - AffectedSelectorEnabled = 1 << 1, - AffectedSelectorDisabled = 1 << 2, - AffectedSelectorIndeterminate = 1 << 3, - AffectedSelectorLink = 1 << 4, - AffectedSelectorTarget = 1 << 5, - AffectedSelectorVisited = 1 << 6 -}; -typedef int AffectedSelectorMask; +class RenderNamedFlowFragment; +class RenderTreePosition; +class WebAnimation; +struct ElementStyle; enum SpellcheckAttributeState { SpellcheckAttributeTrue, @@ -67,113 +63,57 @@ enum SpellcheckAttributeState { SpellcheckAttributeDefault }; +enum class SelectionRevealMode { + Reveal, + RevealUpToMainFrame, // Scroll overflow and iframes, but not the main frame. + DoNotReveal +}; + class Element : public ContainerNode { public: - static PassRefPtr<Element> create(const QualifiedName&, Document&); + static Ref<Element> create(const QualifiedName&, Document&); virtual ~Element(); - DEFINE_ATTRIBUTE_EVENT_LISTENER(abort); - DEFINE_ATTRIBUTE_EVENT_LISTENER(change); - DEFINE_ATTRIBUTE_EVENT_LISTENER(click); - DEFINE_ATTRIBUTE_EVENT_LISTENER(contextmenu); - DEFINE_ATTRIBUTE_EVENT_LISTENER(dblclick); - DEFINE_ATTRIBUTE_EVENT_LISTENER(dragenter); - DEFINE_ATTRIBUTE_EVENT_LISTENER(dragover); - DEFINE_ATTRIBUTE_EVENT_LISTENER(dragleave); - DEFINE_ATTRIBUTE_EVENT_LISTENER(drop); - DEFINE_ATTRIBUTE_EVENT_LISTENER(dragstart); - DEFINE_ATTRIBUTE_EVENT_LISTENER(drag); - DEFINE_ATTRIBUTE_EVENT_LISTENER(dragend); - DEFINE_ATTRIBUTE_EVENT_LISTENER(input); - DEFINE_ATTRIBUTE_EVENT_LISTENER(invalid); - DEFINE_ATTRIBUTE_EVENT_LISTENER(keydown); - DEFINE_ATTRIBUTE_EVENT_LISTENER(keypress); - DEFINE_ATTRIBUTE_EVENT_LISTENER(keyup); - DEFINE_ATTRIBUTE_EVENT_LISTENER(mousedown); - DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseenter); - DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseleave); - DEFINE_ATTRIBUTE_EVENT_LISTENER(mousemove); - DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseout); - DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseover); - DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseup); - DEFINE_ATTRIBUTE_EVENT_LISTENER(mousewheel); - DEFINE_ATTRIBUTE_EVENT_LISTENER(scroll); - DEFINE_ATTRIBUTE_EVENT_LISTENER(select); - DEFINE_ATTRIBUTE_EVENT_LISTENER(submit); - DEFINE_ATTRIBUTE_EVENT_LISTENER(wheel); - - // These four attribute event handler attributes are overridden by HTMLBodyElement - // and HTMLFrameSetElement to forward to the DOMWindow. - DECLARE_VIRTUAL_ATTRIBUTE_EVENT_LISTENER(blur); - DECLARE_VIRTUAL_ATTRIBUTE_EVENT_LISTENER(error); - DECLARE_VIRTUAL_ATTRIBUTE_EVENT_LISTENER(focus); - DECLARE_VIRTUAL_ATTRIBUTE_EVENT_LISTENER(load); - - // WebKit extensions - DEFINE_ATTRIBUTE_EVENT_LISTENER(beforecut); - DEFINE_ATTRIBUTE_EVENT_LISTENER(cut); - DEFINE_ATTRIBUTE_EVENT_LISTENER(beforecopy); - DEFINE_ATTRIBUTE_EVENT_LISTENER(copy); - DEFINE_ATTRIBUTE_EVENT_LISTENER(beforepaste); - DEFINE_ATTRIBUTE_EVENT_LISTENER(paste); - DEFINE_ATTRIBUTE_EVENT_LISTENER(reset); - DEFINE_ATTRIBUTE_EVENT_LISTENER(search); - DEFINE_ATTRIBUTE_EVENT_LISTENER(selectstart); -#if ENABLE(TOUCH_EVENTS) - DEFINE_ATTRIBUTE_EVENT_LISTENER(touchstart); - DEFINE_ATTRIBUTE_EVENT_LISTENER(touchmove); - DEFINE_ATTRIBUTE_EVENT_LISTENER(touchend); - DEFINE_ATTRIBUTE_EVENT_LISTENER(touchcancel); -#endif -#if ENABLE(IOS_GESTURE_EVENTS) - DEFINE_ATTRIBUTE_EVENT_LISTENER(gesturestart); - DEFINE_ATTRIBUTE_EVENT_LISTENER(gesturechange); - DEFINE_ATTRIBUTE_EVENT_LISTENER(gestureend); -#endif -#if ENABLE(FULLSCREEN_API) - DEFINE_ATTRIBUTE_EVENT_LISTENER(webkitfullscreenchange); - DEFINE_ATTRIBUTE_EVENT_LISTENER(webkitfullscreenerror); -#endif - - bool hasAttribute(const QualifiedName&) const; - const AtomicString& getAttribute(const QualifiedName&) const; - void setAttribute(const QualifiedName&, const AtomicString& value); + WEBCORE_EXPORT bool hasAttribute(const QualifiedName&) const; + WEBCORE_EXPORT const AtomicString& getAttribute(const QualifiedName&) const; + WEBCORE_EXPORT void setAttribute(const QualifiedName&, const AtomicString& value); + WEBCORE_EXPORT void setAttributeWithoutSynchronization(const QualifiedName&, const AtomicString& value); void setSynchronizedLazyAttribute(const QualifiedName&, const AtomicString& value); - void removeAttribute(const QualifiedName&); + bool removeAttribute(const QualifiedName&); + Vector<String> getAttributeNames() const; // Typed getters and setters for language bindings. - int getIntegralAttribute(const QualifiedName& attributeName) const; - void setIntegralAttribute(const QualifiedName& attributeName, int value); - unsigned getUnsignedIntegralAttribute(const QualifiedName& attributeName) const; - void setUnsignedIntegralAttribute(const QualifiedName& attributeName, unsigned value); + WEBCORE_EXPORT int getIntegralAttribute(const QualifiedName& attributeName) const; + WEBCORE_EXPORT void setIntegralAttribute(const QualifiedName& attributeName, int value); + WEBCORE_EXPORT unsigned getUnsignedIntegralAttribute(const QualifiedName& attributeName) const; + WEBCORE_EXPORT void setUnsignedIntegralAttribute(const QualifiedName& attributeName, unsigned value); // Call this to get the value of an attribute that is known not to be the style // attribute or one of the SVG animatable attributes. - bool fastHasAttribute(const QualifiedName&) const; - const AtomicString& fastGetAttribute(const QualifiedName&) const; + bool hasAttributeWithoutSynchronization(const QualifiedName&) const; + const AtomicString& attributeWithoutSynchronization(const QualifiedName&) const; #ifndef NDEBUG - bool fastAttributeLookupAllowed(const QualifiedName&) const; + WEBCORE_EXPORT bool fastAttributeLookupAllowed(const QualifiedName&) const; #endif #ifdef DUMP_NODE_STATISTICS bool hasNamedNodeMap() const; #endif - bool hasAttributes() const; + WEBCORE_EXPORT bool hasAttributes() const; // This variant will not update the potentially invalid attributes. To be used when not interested // in style attribute or one of the SVG animation attributes. bool hasAttributesWithoutUpdate() const; - bool hasAttribute(const AtomicString& name) const; - bool hasAttributeNS(const AtomicString& namespaceURI, const AtomicString& localName) const; + WEBCORE_EXPORT bool hasAttribute(const AtomicString& name) const; + WEBCORE_EXPORT bool hasAttributeNS(const AtomicString& namespaceURI, const AtomicString& localName) const; - const AtomicString& getAttribute(const AtomicString& name) const; - const AtomicString& getAttributeNS(const AtomicString& namespaceURI, const AtomicString& localName) const; + WEBCORE_EXPORT const AtomicString& getAttribute(const AtomicString& name) const; + WEBCORE_EXPORT const AtomicString& getAttributeNS(const AtomicString& namespaceURI, const AtomicString& localName) const; - void setAttribute(const AtomicString& name, const AtomicString& value, ExceptionCode&); - static bool parseAttributeName(QualifiedName&, const AtomicString& namespaceURI, const AtomicString& qualifiedName, ExceptionCode&); - void setAttributeNS(const AtomicString& namespaceURI, const AtomicString& qualifiedName, const AtomicString& value, ExceptionCode&); + WEBCORE_EXPORT ExceptionOr<void> setAttribute(const AtomicString& name, const AtomicString& value); + static ExceptionOr<QualifiedName> parseAttributeName(const AtomicString& namespaceURI, const AtomicString& qualifiedName); + WEBCORE_EXPORT ExceptionOr<void> setAttributeNS(const AtomicString& namespaceURI, const AtomicString& qualifiedName, const AtomicString& value); - bool isIdAttributeName(const QualifiedName&) const; const AtomicString& getIdAttribute() const; void setIdAttribute(const AtomicString&); @@ -193,26 +133,37 @@ public: unsigned findAttributeIndexByName(const QualifiedName& name) const { return elementData()->findAttributeIndexByName(name); } unsigned findAttributeIndexByName(const AtomicString& name, bool shouldIgnoreAttributeCase) const { return elementData()->findAttributeIndexByName(name, shouldIgnoreAttributeCase); } - void scrollIntoView(bool alignToTop = true); - void scrollIntoViewIfNeeded(bool centerIfNeeded = true); + WEBCORE_EXPORT void scrollIntoView(bool alignToTop = true); + WEBCORE_EXPORT void scrollIntoViewIfNeeded(bool centerIfNeeded = true); + WEBCORE_EXPORT void scrollIntoViewIfNotVisible(bool centerIfNotVisible = true); - void scrollByLines(int lines); - void scrollByPages(int pages); + void scrollBy(const ScrollToOptions&); + void scrollBy(double x, double y); + virtual void scrollTo(const ScrollToOptions&); + void scrollTo(double x, double y); - int offsetLeft(); - int offsetTop(); - int offsetWidth(); - int offsetHeight(); + WEBCORE_EXPORT void scrollByLines(int lines); + WEBCORE_EXPORT void scrollByPages(int pages); + + WEBCORE_EXPORT double offsetLeft(); + WEBCORE_EXPORT double offsetTop(); + WEBCORE_EXPORT double offsetWidth(); + WEBCORE_EXPORT double offsetHeight(); + + bool mayCauseRepaintInsideViewport(const IntRect* visibleRect = nullptr) const; // FIXME: Replace uses of offsetParent in the platform with calls // to the render layer and merge bindingsOffsetParent and offsetParent. - Element* bindingsOffsetParent(); + WEBCORE_EXPORT Element* bindingsOffsetParent(); + + const Element* rootElement() const; Element* offsetParent(); - int clientLeft(); - int clientTop(); - int clientWidth(); - int clientHeight(); + WEBCORE_EXPORT double clientLeft(); + WEBCORE_EXPORT double clientTop(); + WEBCORE_EXPORT double clientWidth(); + WEBCORE_EXPORT double clientHeight(); + virtual int scrollLeft(); virtual int scrollTop(); virtual void setScrollLeft(int); @@ -220,63 +171,66 @@ public: virtual int scrollWidth(); virtual int scrollHeight(); - IntRect boundsInRootViewSpace(); + WEBCORE_EXPORT IntRect boundsInRootViewSpace(); + + Ref<ClientRectList> getClientRects(); + Ref<ClientRect> getBoundingClientRect(); - PassRefPtr<ClientRectList> getClientRects(); - PassRefPtr<ClientRect> getBoundingClientRect(); - // Returns the absolute bounding box translated into client coordinates. - IntRect clientRect() const; + WEBCORE_EXPORT IntRect clientRect() const; // Returns the absolute bounding box translated into screen coordinates. - IntRect screenRect() const; + WEBCORE_EXPORT IntRect screenRect() const; - void removeAttribute(const AtomicString& name); - void removeAttributeNS(const AtomicString& namespaceURI, const AtomicString& localName); + WEBCORE_EXPORT bool removeAttribute(const AtomicString& name); + WEBCORE_EXPORT bool removeAttributeNS(const AtomicString& namespaceURI, const AtomicString& localName); - PassRefPtr<Attr> detachAttribute(unsigned index); + Ref<Attr> detachAttribute(unsigned index); - PassRefPtr<Attr> getAttributeNode(const AtomicString& name); - PassRefPtr<Attr> getAttributeNodeNS(const AtomicString& namespaceURI, const AtomicString& localName); - PassRefPtr<Attr> setAttributeNode(Attr*, ExceptionCode&); - PassRefPtr<Attr> setAttributeNodeNS(Attr*, ExceptionCode&); - PassRefPtr<Attr> removeAttributeNode(Attr*, ExceptionCode&); + WEBCORE_EXPORT RefPtr<Attr> getAttributeNode(const AtomicString& name); + WEBCORE_EXPORT RefPtr<Attr> getAttributeNodeNS(const AtomicString& namespaceURI, const AtomicString& localName); + WEBCORE_EXPORT ExceptionOr<RefPtr<Attr>> setAttributeNode(Attr&); + WEBCORE_EXPORT ExceptionOr<RefPtr<Attr>> setAttributeNodeNS(Attr&); + WEBCORE_EXPORT ExceptionOr<Ref<Attr>> removeAttributeNode(Attr&); - PassRefPtr<Attr> attrIfExists(const QualifiedName&); - PassRefPtr<Attr> ensureAttr(const QualifiedName&); + RefPtr<Attr> attrIfExists(const QualifiedName&); + RefPtr<Attr> attrIfExists(const AtomicString& localName, bool shouldIgnoreAttributeCase); + Ref<Attr> ensureAttr(const QualifiedName&); const Vector<RefPtr<Attr>>& attrNodeList(); - virtual CSSStyleDeclaration* style(); + virtual CSSStyleDeclaration* cssomStyle(); const QualifiedName& tagQName() const { return m_tagName; } -#if ENABLE(CSS_SELECTOR_JIT) +#if ENABLE(JIT) static ptrdiff_t tagQNameMemoryOffset() { return OBJECT_OFFSETOF(Element, m_tagName); } -#endif // ENABLE(CSS_SELECTOR_JIT) +#endif // ENABLE(JIT) String tagName() const { return nodeName(); } bool hasTagName(const QualifiedName& tagName) const { return m_tagName.matches(tagName); } - + bool hasTagName(const HTMLQualifiedName& tagName) const { return ContainerNode::hasTagName(tagName); } + bool hasTagName(const MathMLQualifiedName& tagName) const { return ContainerNode::hasTagName(tagName); } + bool hasTagName(const SVGQualifiedName& tagName) const { return ContainerNode::hasTagName(tagName); } + // A fast function for checking the local name against another atomic string. bool hasLocalName(const AtomicString& other) const { return m_tagName.localName() == other; } - bool hasLocalName(const QualifiedName& other) const { return m_tagName.localName() == other.localName(); } - virtual const AtomicString& localName() const override final { return m_tagName.localName(); } - virtual const AtomicString& prefix() const override final { return m_tagName.prefix(); } - virtual const AtomicString& namespaceURI() const override final { return m_tagName.namespaceURI(); } + const AtomicString& localName() const final { return m_tagName.localName(); } + const AtomicString& prefix() const final { return m_tagName.prefix(); } + const AtomicString& namespaceURI() const final { return m_tagName.namespaceURI(); } - virtual URL baseURI() const override final; + ExceptionOr<void> setPrefix(const AtomicString&) final; - virtual String nodeName() const override; + String nodeName() const override; - PassRefPtr<Element> cloneElementWithChildren(); - PassRefPtr<Element> cloneElementWithoutChildren(); + Ref<Element> cloneElementWithChildren(Document&); + Ref<Element> cloneElementWithoutChildren(Document&); void normalizeAttributes(); String nodeNamePreservingCase() const; - void setBooleanAttribute(const QualifiedName& name, bool); + WEBCORE_EXPORT void setBooleanAttribute(const QualifiedName& name, bool); // For exposing to DOM only. - NamedNodeMap* attributes() const; + WEBCORE_EXPORT NamedNodeMap& attributes() const; enum AttributeModificationReason { ModifiedDirectly, @@ -309,78 +263,88 @@ public: virtual void copyNonAttributePropertiesFromElement(const Element&) { } - void lazyReattach(); - - virtual RenderPtr<RenderElement> createElementRenderer(PassRef<RenderStyle>); + virtual RenderPtr<RenderElement> createElementRenderer(RenderStyle&&, const RenderTreePosition&); virtual bool rendererIsNeeded(const RenderStyle&); - void didAffectSelector(AffectedSelectorMask); - ShadowRoot* shadowRoot() const; - PassRefPtr<ShadowRoot> createShadowRoot(ExceptionCode&); - ShadowRoot* authorShadowRoot() const; + WEBCORE_EXPORT ShadowRoot* shadowRoot() const; + ShadowRoot* shadowRootForBindings(JSC::ExecState&) const; - bool hasAuthorShadowRoot() const { return authorShadowRoot(); } + struct ShadowRootInit { + ShadowRootMode mode; + }; + ExceptionOr<ShadowRoot&> attachShadow(const ShadowRootInit&); ShadowRoot* userAgentShadowRoot() const; - ShadowRoot& ensureUserAgentShadowRoot(); + WEBCORE_EXPORT ShadowRoot& ensureUserAgentShadowRoot(); + + void setIsDefinedCustomElement(JSCustomElementInterface&); + void setIsFailedCustomElement(JSCustomElementInterface&); + void setIsCustomElementUpgradeCandidate(); + void enqueueToUpgrade(JSCustomElementInterface&); + CustomElementReactionQueue* reactionQueue() const; + // FIXME: this should not be virtual, do not override this. virtual const AtomicString& shadowPseudoId() const; bool inActiveChain() const { return isUserActionElement() && isUserActionElementInActiveChain(); } bool active() const { return isUserActionElement() && isUserActionElementActive(); } bool hovered() const { return isUserActionElement() && isUserActionElementHovered(); } bool focused() const { return isUserActionElement() && isUserActionElementFocused(); } + bool hasFocusWithin() const { return getFlag(HasFocusWithin); }; virtual void setActive(bool flag = true, bool pause = false); virtual void setHovered(bool flag = true); virtual void setFocus(bool flag); + void setHasFocusWithin(bool flag); + bool tabIndexSetExplicitly() const; virtual bool supportsFocus() const; virtual bool isFocusable() const; - virtual bool isKeyboardFocusable(KeyboardEvent*) const; + virtual bool isKeyboardFocusable(KeyboardEvent&) const; virtual bool isMouseFocusable() const; virtual bool shouldUseInputMethod(); - virtual short tabIndex() const; + virtual int tabIndex() const; + WEBCORE_EXPORT void setTabIndex(int); virtual Element* focusDelegate(); - virtual RenderStyle* computedStyle(PseudoId = NOPSEUDO) override; + WEBCORE_EXPORT ExceptionOr<Element*> insertAdjacentElement(const String& where, Element& newChild); + WEBCORE_EXPORT ExceptionOr<void> insertAdjacentHTML(const String& where, const String& html); + WEBCORE_EXPORT ExceptionOr<void> insertAdjacentText(const String& where, const String& text); + + const RenderStyle* computedStyle(PseudoId = NOPSEUDO) override; + + bool needsStyleInvalidation() const; // Methods for indicating the style is affected by dynamic updates (e.g., children changing, our position changing in our sibling list, etc.) + bool styleAffectedByActive() const { return hasRareData() && rareDataStyleAffectedByActive(); } bool styleAffectedByEmpty() const { return hasRareData() && rareDataStyleAffectedByEmpty(); } + bool styleAffectedByFocusWithin() const { return hasRareData() && rareDataStyleAffectedByFocusWithin(); } bool childrenAffectedByHover() const { return getFlag(ChildrenAffectedByHoverRulesFlag); } - bool childrenAffectedByActive() const { return hasRareData() && rareDataChildrenAffectedByActive(); } bool childrenAffectedByDrag() const { return hasRareData() && rareDataChildrenAffectedByDrag(); } - bool childrenAffectedByPositionalRules() const { return hasRareData() && (rareDataChildrenAffectedByForwardPositionalRules() || rareDataChildrenAffectedByBackwardPositionalRules()); } bool childrenAffectedByFirstChildRules() const { return getFlag(ChildrenAffectedByFirstChildRulesFlag); } bool childrenAffectedByLastChildRules() const { return getFlag(ChildrenAffectedByLastChildRulesFlag); } - bool childrenAffectedByDirectAdjacentRules() const { return getFlag(ChildrenAffectedByDirectAdjacentRulesFlag); } - bool childrenAffectedByForwardPositionalRules() const { return hasRareData() && rareDataChildrenAffectedByForwardPositionalRules(); } bool childrenAffectedByBackwardPositionalRules() const { return hasRareData() && rareDataChildrenAffectedByBackwardPositionalRules(); } + bool childrenAffectedByPropertyBasedBackwardPositionalRules() const { return hasRareData() && rareDataChildrenAffectedByPropertyBasedBackwardPositionalRules(); } + bool affectsNextSiblingElementStyle() const { return getFlag(AffectsNextSiblingElementStyle); } unsigned childIndex() const { return hasRareData() ? rareDataChildIndex() : 0; } bool hasFlagsSetDuringStylingOfChildren() const; void setStyleAffectedByEmpty(); + void setStyleAffectedByFocusWithin(); void setChildrenAffectedByHover() { setFlag(ChildrenAffectedByHoverRulesFlag); } - void setChildrenAffectedByActive(); + void setStyleAffectedByActive(); void setChildrenAffectedByDrag(); void setChildrenAffectedByFirstChildRules() { setFlag(ChildrenAffectedByFirstChildRulesFlag); } void setChildrenAffectedByLastChildRules() { setFlag(ChildrenAffectedByLastChildRulesFlag); } - static void setChildrenAffectedByDirectAdjacentRules(Element*); - void setChildrenAffectedByDirectAdjacentRules() { setFlag(ChildrenAffectedByDirectAdjacentRulesFlag); } - static void setChildrenAffectedByForwardPositionalRules(Element*); - void setChildrenAffectedByForwardPositionalRules() { setChildrenAffectedByForwardPositionalRules(this); } void setChildrenAffectedByBackwardPositionalRules(); + void setChildrenAffectedByPropertyBasedBackwardPositionalRules(); + void setAffectsNextSiblingElementStyle() { setFlag(AffectsNextSiblingElementStyle); } + void setStyleIsAffectedByPreviousSibling() { setFlag(StyleIsAffectedByPreviousSibling); } void setChildIndex(unsigned); - void setIsInCanvasSubtree(bool); - bool isInCanvasSubtree() const; - - void setIsInsideRegion(bool); - bool isInsideRegion() const; - void setRegionOversetState(RegionOversetState); RegionOversetState regionOversetState() const; @@ -390,33 +354,40 @@ public: virtual void accessKeyAction(bool /*sendToAnyEvent*/) { } virtual bool isURLAttribute(const Attribute&) const { return false; } + virtual bool attributeContainsURL(const Attribute& attribute) const { return isURLAttribute(attribute); } + virtual String completeURLsInAttributeValue(const URL& base, const Attribute&) const; virtual bool isHTMLContentAttribute(const Attribute&) const { return false; } - URL getURLAttribute(const QualifiedName&) const; + WEBCORE_EXPORT URL getURLAttribute(const QualifiedName&) const; URL getNonEmptyURLAttribute(const QualifiedName&) const; virtual const AtomicString& imageSourceURL() const; virtual String target() const { return String(); } + static AXTextStateChangeIntent defaultFocusTextStateChangeIntent() { return AXTextStateChangeIntent(AXTextStateChangeTypeSelectionMove, AXTextSelection { AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityUnknown, true }); } void updateFocusAppearanceAfterAttachIfNeeded(); virtual void focus(bool restorePreviousSelection = true, FocusDirection = FocusDirectionNone); - virtual void updateFocusAppearance(bool restorePreviousSelection); + virtual void updateFocusAppearance(SelectionRestorationMode, SelectionRevealMode = SelectionRevealMode::Reveal); virtual void blur(); - String innerText(); - String outerText(); + WEBCORE_EXPORT String innerHTML() const; + WEBCORE_EXPORT String outerHTML() const; + WEBCORE_EXPORT ExceptionOr<void> setInnerHTML(const String&); + WEBCORE_EXPORT ExceptionOr<void> setOuterHTML(const String&); + WEBCORE_EXPORT String innerText(); + WEBCORE_EXPORT String outerText(); virtual String title() const; const AtomicString& pseudo() const; - void setPseudo(const AtomicString&); + WEBCORE_EXPORT void setPseudo(const AtomicString&); LayoutSize minimumSizeForResizing() const; void setMinimumSizeForResizing(const LayoutSize&); // Use Document::registerForDocumentActivationCallbacks() to subscribe to these - virtual void documentWillSuspendForPageCache() { } - virtual void documentDidResumeFromPageCache() { } + virtual void prepareForDocumentSuspension() { } + virtual void resumeFromDocumentSuspension() { } // Use Document::registerForMediaVolumeCallbacks() to subscribe to this virtual void mediaVolumeDidChange() { } @@ -424,113 +395,87 @@ public: // Use Document::registerForPrivateBrowsingStateChangedCallbacks() to subscribe to this. virtual void privateBrowsingStateDidChange() { } + virtual void willBecomeFullscreenElement(); + virtual void ancestorWillEnterFullscreen() { } virtual void didBecomeFullscreenElement() { } virtual void willStopBeingFullscreenElement() { } -#if ENABLE(PAGE_VISIBILITY_API) // Use Document::registerForVisibilityStateChangedCallbacks() to subscribe to this. virtual void visibilityStateChanged() { } -#endif #if ENABLE(VIDEO_TRACK) virtual void captionPreferencesChanged() { } #endif bool isFinishedParsingChildren() const { return isParsingChildrenFinished(); } - virtual void finishParsingChildren() override; - virtual void beginParsingChildren() override final; + void finishParsingChildren() override; + void beginParsingChildren() final; - PseudoElement* beforePseudoElement() const; - PseudoElement* afterPseudoElement() const; + WEBCORE_EXPORT PseudoElement* beforePseudoElement() const; + WEBCORE_EXPORT PseudoElement* afterPseudoElement() const; bool childNeedsShadowWalker() const; void didShadowTreeAwareChildrenChange(); - // ElementTraversal API - Element* firstElementChild() const; - Element* lastElementChild() const; - Element* previousElementSibling() const; - Element* nextElementSibling() const; - unsigned childElementCount() const; - - virtual bool matchesReadOnlyPseudoClass() const; + virtual bool matchesValidPseudoClass() const; + virtual bool matchesInvalidPseudoClass() const; virtual bool matchesReadWritePseudoClass() const; - bool webkitMatchesSelector(const String& selectors, ExceptionCode&); + virtual bool matchesIndeterminatePseudoClass() const; + virtual bool matchesDefaultPseudoClass() const; + WEBCORE_EXPORT ExceptionOr<bool> matches(const String& selectors); + WEBCORE_EXPORT ExceptionOr<Element*> closest(const String& selectors); virtual bool shouldAppearIndeterminate() const; - DOMTokenList* classList(); + WEBCORE_EXPORT DOMTokenList& classList(); - DatasetDOMStringMap* dataset(); + DatasetDOMStringMap& dataset(); #if ENABLE(VIDEO) virtual bool isMediaElement() const { return false; } #endif -#if ENABLE(INPUT_SPEECH) - virtual bool isInputFieldSpeechButtonElement() const { return false; } -#endif - virtual bool isFormControlElement() const { return false; } virtual bool isSpinButtonElement() const { return false; } virtual bool isTextFormControl() const { return false; } virtual bool isOptionalFormControl() const { return false; } virtual bool isRequiredFormControl() const { return false; } - virtual bool isDefaultButtonForForm() const { return false; } - virtual bool willValidate() const { return false; } - virtual bool isValidFormControlElement() { return false; } virtual bool isInRange() const { return false; } virtual bool isOutOfRange() const { return false; } virtual bool isFrameElementBase() const { return false; } - virtual bool isSearchFieldCancelButtonElement() const { return false; } + virtual bool isUploadButton() const { return false; } + virtual bool isSliderContainerElement() const { return false; } - virtual bool canContainRangeEndPoint() const override { return true; } + bool canContainRangeEndPoint() const override; // Used for disabled form elements; if true, prevents mouse events from being dispatched // to event listeners, and prevents DOMActivate events from being sent at all. virtual bool isDisabledFormControl() const { return false; } - virtual bool childShouldCreateRenderer(const Node&) const override; + virtual bool childShouldCreateRenderer(const Node&) const; -#if ENABLE(SVG) bool hasPendingResources() const; void setHasPendingResources(); void clearHasPendingResources(); virtual void buildPendingResource() { }; -#endif #if ENABLE(FULLSCREEN_API) - enum { - ALLOW_KEYBOARD_INPUT = 1 << 0, - LEGACY_MOZILLA_REQUEST = 1 << 1, - }; - - void webkitRequestFullScreen(unsigned short flags); - bool containsFullScreenElement() const; + WEBCORE_EXPORT bool containsFullScreenElement() const; void setContainsFullScreenElement(bool); void setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(bool); - - // W3C API - void webkitRequestFullscreen(); + WEBCORE_EXPORT void webkitRequestFullscreen(); #endif #if ENABLE(POINTER_LOCK) - void webkitRequestPointerLock(); + WEBCORE_EXPORT void requestPointerLock(); #endif -#if ENABLE(INDIE_UI) - void setUIActions(const AtomicString&); - const AtomicString& UIActions() const; -#endif - - virtual bool isSpellCheckingEnabled() const; - - PassRef<RenderStyle> styleForRenderer(); + bool isSpellCheckingEnabled() const; - RenderRegion* renderRegion() const; + RenderNamedFlowFragment* renderNamedFlowFragment() const; #if ENABLE(CSS_REGIONS) virtual bool shouldMoveToFlowThread(const RenderStyle&) const; - const AtomicString& webkitRegionOverset() const; + WEBCORE_EXPORT const AtomicString& webkitRegionOverset() const; Vector<RefPtr<Range>> webkitGetRegionFlowRanges() const; #endif @@ -539,58 +484,109 @@ public: bool hasName() const; const SpaceSplitString& classNames() const; - IntSize savedLayerScrollOffset() const; - void setSavedLayerScrollOffset(const IntSize&); + IntPoint savedLayerScrollPosition() const; + void setSavedLayerScrollPosition(const IntPoint&); bool dispatchMouseEvent(const PlatformMouseEvent&, const AtomicString& eventType, int clickCount = 0, Element* relatedTarget = nullptr); bool dispatchWheelEvent(const PlatformWheelEvent&); bool dispatchKeyEvent(const PlatformKeyboardEvent&); void dispatchSimulatedClick(Event* underlyingEvent, SimulatedClickMouseEventOptions = SendNoEvents, SimulatedClickVisualOptions = ShowPressedLook); - void dispatchFocusInEvent(const AtomicString& eventType, PassRefPtr<Element> oldFocusedElement); - void dispatchFocusOutEvent(const AtomicString& eventType, PassRefPtr<Element> newFocusedElement); - virtual void dispatchFocusEvent(PassRefPtr<Element> oldFocusedElement, FocusDirection); - virtual void dispatchBlurEvent(PassRefPtr<Element> newFocusedElement); + void dispatchFocusInEvent(const AtomicString& eventType, RefPtr<Element>&& oldFocusedElement); + void dispatchFocusOutEvent(const AtomicString& eventType, RefPtr<Element>&& newFocusedElement); + virtual void dispatchFocusEvent(RefPtr<Element>&& oldFocusedElement, FocusDirection); + virtual void dispatchBlurEvent(RefPtr<Element>&& newFocusedElement); - virtual bool willRecalcStyle(Style::Change); + WEBCORE_EXPORT bool dispatchMouseForceWillBegin(); + + virtual void willRecalcStyle(Style::Change); virtual void didRecalcStyle(Style::Change); + virtual void willResetComputedStyle(); virtual void willAttachRenderers(); virtual void didAttachRenderers(); virtual void willDetachRenderers(); virtual void didDetachRenderers(); + virtual std::optional<ElementStyle> resolveCustomStyle(const RenderStyle& parentStyle, const RenderStyle* shadowHostStyle); + + LayoutRect absoluteEventHandlerBounds(bool& includesFixedPositionElements) override; - void setBeforePseudoElement(PassRefPtr<PseudoElement>); - void setAfterPseudoElement(PassRefPtr<PseudoElement>); + void setBeforePseudoElement(Ref<PseudoElement>&&); + void setAfterPseudoElement(Ref<PseudoElement>&&); void clearBeforePseudoElement(); void clearAfterPseudoElement(); void resetComputedStyle(); + void resetStyleRelations(); void clearStyleDerivedDataBeforeDetachingRenderer(); void clearHoverAndActiveStatusBeforeDetachingRenderer(); -protected: - Element(const QualifiedName& tagName, Document& document, ConstructionType type) - : ContainerNode(&document, type) - , m_tagName(tagName) - { - } + WEBCORE_EXPORT URL absoluteLinkURL() const; + +#if ENABLE(TOUCH_EVENTS) + bool allowsDoubleTapGesture() const override; +#endif + + StyleResolver& styleResolver(); + ElementStyle resolveStyle(const RenderStyle* parentStyle); + + // Invalidates the style of a single element. Style is resolved lazily. + // Descendant elements are resolved as needed, for example if an inherited property changes. + // This should be called whenever an element changes in a manner that can affect its style. + void invalidateStyle(); + + // As above but also call RenderElement::setStyle with StyleDifferenceRecompositeLayer flag for + // the element even when the style doesn't change. This is mostly needed by the animation code. + WEBCORE_EXPORT void invalidateStyleAndLayerComposition(); + + // Invalidate the element and all its descendants. This is used when there is some sort of change + // in the tree that may affect the style of any of the descendants and we don't know how to optimize + // the case to limit the scope. This is expensive and should be avoided. + void invalidateStyleForSubtree(); - virtual InsertionNotificationRequest insertedInto(ContainerNode&) override; - virtual void removedFrom(ContainerNode&) override; - virtual void childrenChanged(const ChildChange&) override; - virtual void removeAllEventListeners() override final; + // Invalidates renderers for the element and all its descendants causing them to be torn down + // and rebuild during style resolution. Style is also recomputed. This is used in code dealing with + // custom (not style based) renderers. This is expensive and should be avoided. + // Elements newly added to the tree are also in this state. + void invalidateStyleAndRenderersForSubtree(); - virtual PassRefPtr<RenderStyle> customStyleForRenderer(); + bool hasDisplayContents() const; + void setHasDisplayContents(bool); - void clearTabIndexExplicitlyIfNeeded(); - void setTabIndexExplicitly(short); + virtual void isVisibleInViewportChanged() { } - PassRefPtr<HTMLCollection> ensureCachedHTMLCollection(CollectionType); - HTMLCollection* cachedHTMLCollection(CollectionType); + using ContainerNode::setAttributeEventListener; + void setAttributeEventListener(const AtomicString& eventType, const QualifiedName& attributeName, const AtomicString& value); + + bool isNamedFlowContentElement() const { return hasRareData() && rareDataIsNamedFlowContentElement(); } + void setIsNamedFlowContentElement(); + void clearIsNamedFlowContentElement(); + +#if ENABLE(WEB_ANIMATIONS) + Vector<WebAnimation*> getAnimations(); +#endif + + Element* findAnchorElementForLink(String& outAnchorName); + +protected: + Element(const QualifiedName&, Document&, ConstructionType); + + InsertionNotificationRequest insertedInto(ContainerNode&) override; + void removedFrom(ContainerNode&) override; + void childrenChanged(const ChildChange&) override; + void removeAllEventListeners() final; + virtual void parserDidSetAttributes(); + void didMoveToNewDocument(Document&) override; + + void clearTabIndexExplicitlyIfNeeded(); + void setTabIndexExplicitly(int); // classAttributeChanged() exists to share code between // parseAttribute (called via setAttribute()) and // svgAttributeChanged (called when element.className.baseValue is set) void classAttributeChanged(const AtomicString& newClassString); + void addShadowRoot(Ref<ShadowRoot>&&); + + static ExceptionOr<void> mergeWithNextTextNode(Text&); + private: bool isTextNode() const; @@ -599,9 +595,6 @@ private: bool isUserActionElementFocused() const; bool isUserActionElementHovered() const; - void resetNeedsNodeRenderingTraversalSlowPath(); - - virtual bool areAuthorShadowsAllowed() const { return true; } virtual void didAddUserAgentShadowRoot(ShadowRoot*) { } virtual bool alwaysCreateUserAgentShadowRoot() const { return false; } @@ -630,37 +623,44 @@ private: void updateIdForDocument(HTMLDocument&, const AtomicString& oldId, const AtomicString& newId, HTMLDocumentNamedItemMapsUpdatingCondition); void updateLabel(TreeScope&, const AtomicString& oldForAttributeValue, const AtomicString& newForAttributeValue); + ExceptionOr<Node*> insertAdjacent(const String& where, Ref<Node>&& newChild); + void scrollByUnits(int units, ScrollGranularity); - virtual void setPrefix(const AtomicString&, ExceptionCode&) override final; - virtual NodeType nodeType() const override final; - virtual bool childTypeAllowed(NodeType) const override final; + NodeType nodeType() const final; + bool childTypeAllowed(NodeType) const final; void setAttributeInternal(unsigned index, const QualifiedName&, const AtomicString& value, SynchronizationOfLazyAttribute); void addAttributeInternal(const QualifiedName&, const AtomicString& value, SynchronizationOfLazyAttribute); void removeAttributeInternal(unsigned index, SynchronizationOfLazyAttribute); -#ifndef NDEBUG - virtual void formatForDebugger(char* buffer, unsigned length) const override; + LayoutRect absoluteEventBounds(bool& boundsIncludeAllDescendantElements, bool& includesFixedPositionElements); + LayoutRect absoluteEventBoundsOfElementAndDescendants(bool& includesFixedPositionElements); + +#if ENABLE(TREE_DEBUGGING) + void formatForDebugger(char* buffer, unsigned length) const override; #endif void cancelFocusAppearanceUpdate(); - // cloneNode is private so that non-virtual cloneElementWithChildren and cloneElementWithoutChildren - // are used instead. - virtual PassRefPtr<Node> cloneNode(bool deep) override; - virtual PassRefPtr<Element> cloneElementWithoutAttributesAndChildren(); + // The cloneNode function is private so that non-virtual cloneElementWith/WithoutChildren are used instead. + Ref<Node> cloneNodeInternal(Document&, CloningOperation) override; + virtual Ref<Element> cloneElementWithoutAttributesAndChildren(Document&); - void addShadowRoot(PassRefPtr<ShadowRoot>); void removeShadowRoot(); + const RenderStyle* existingComputedStyle(); + const RenderStyle& resolveComputedStyle(); + bool rareDataStyleAffectedByEmpty() const; + bool rareDataStyleAffectedByFocusWithin() const; + bool rareDataIsNamedFlowContentElement() const; bool rareDataChildrenAffectedByHover() const; - bool rareDataChildrenAffectedByActive() const; + bool rareDataStyleAffectedByActive() const; bool rareDataChildrenAffectedByDrag() const; bool rareDataChildrenAffectedByLastChildRules() const; - bool rareDataChildrenAffectedByForwardPositionalRules() const; bool rareDataChildrenAffectedByBackwardPositionalRules() const; + bool rareDataChildrenAffectedByPropertyBasedBackwardPositionalRules() const; unsigned rareDataChildIndex() const; SpellcheckAttributeState spellcheckAttributeState() const; @@ -679,52 +679,47 @@ private: // Anyone thinking of using this should call document instead of ownerDocument. void ownerDocument() const = delete; + + void attachAttributeNodeIfNeeded(Attr&); QualifiedName m_tagName; RefPtr<ElementData> m_elementData; }; -inline bool isElement(const Node& node) { return node.isElementNode(); } - -NODE_TYPE_CASTS(Element) - -template <typename Type> bool isElementOfType(const Element&); -template <typename Type> inline bool isElementOfType(const Node& node) { return node.isElementNode() && isElementOfType<const Type>(toElement(node)); } -template <> inline bool isElementOfType<const Element>(const Element&) { return true; } - -inline bool Node::hasTagName(const QualifiedName& name) const -{ - return isElementNode() && toElement(this)->hasTagName(name); -} - -inline bool Node::hasLocalName(const AtomicString& name) const -{ - return isElementNode() && toElement(this)->hasLocalName(name); -} - inline bool Node::hasAttributes() const { - return isElementNode() && toElement(this)->hasAttributes(); + return is<Element>(*this) && downcast<Element>(*this).hasAttributes(); } inline NamedNodeMap* Node::attributes() const { - return isElementNode() ? toElement(this)->attributes() : nullptr; + return is<Element>(*this) ? &downcast<Element>(*this).attributes() : nullptr; } inline Element* Node::parentElement() const { ContainerNode* parent = parentNode(); - return parent && parent->isElementNode() ? toElement(parent) : nullptr; + return is<Element>(parent) ? downcast<Element>(parent) : nullptr; +} + +inline const Element* Element::rootElement() const +{ + if (isConnected()) + return document().documentElement(); + + const Element* highest = this; + while (highest->parentElement()) + highest = highest->parentElement(); + return highest; } -inline bool Element::fastHasAttribute(const QualifiedName& name) const +inline bool Element::hasAttributeWithoutSynchronization(const QualifiedName& name) const { ASSERT(fastAttributeLookupAllowed(name)); return elementData() && findAttributeByName(name); } -inline const AtomicString& Element::fastGetAttribute(const QualifiedName& name) const +inline const AtomicString& Element::attributeWithoutSynchronization(const QualifiedName& name) const { ASSERT(fastAttributeLookupAllowed(name)); if (elementData()) { @@ -741,32 +736,26 @@ inline bool Element::hasAttributesWithoutUpdate() const inline const AtomicString& Element::idForStyleResolution() const { - ASSERT(hasID()); - return elementData()->idForStyleResolution(); -} - -inline bool Element::isIdAttributeName(const QualifiedName& attributeName) const -{ - // FIXME: This check is probably not correct for the case where the document has an id attribute - // with a non-null namespace, because it will return false, a false negative, if the prefixes - // don't match but the local name and namespace both do. However, since this has been like this - // for a while and the code paths may be hot, we'll have to measure performance if we fix it. - return attributeName == document().idAttributeName(); + return hasID() ? elementData()->idForStyleResolution() : nullAtom; } inline const AtomicString& Element::getIdAttribute() const { - return hasID() ? fastGetAttribute(document().idAttributeName()) : nullAtom; + if (hasID()) + return elementData()->findAttributeByName(HTMLNames::idAttr)->value(); + return nullAtom; } inline const AtomicString& Element::getNameAttribute() const { - return hasName() ? fastGetAttribute(HTMLNames::nameAttr) : nullAtom; + if (hasName()) + return elementData()->findAttributeByName(HTMLNames::nameAttr)->value(); + return nullAtom; } inline void Element::setIdAttribute(const AtomicString& value) { - setAttribute(document().idAttributeName(), value); + setAttributeWithoutSynchronization(HTMLNames::idAttr, value); } inline const SpaceSplitString& Element::classNames() const @@ -816,23 +805,22 @@ inline UniqueElementData& Element::ensureUniqueElementData() return static_cast<UniqueElementData&>(*m_elementData); } -class PostAttachCallbackDisabler { -public: - explicit PostAttachCallbackDisabler(Document& document) - : m_document(document) - { - Element::suspendPostAttachCallbacks(m_document); - } - - ~PostAttachCallbackDisabler() - { - Element::resumePostAttachCallbacks(m_document); - } +inline bool shouldIgnoreAttributeCase(const Element& element) +{ + return element.isHTMLElement() && element.document().isHTMLDocument(); +} -private: - Document& m_document; -}; +inline void Element::setHasFocusWithin(bool flag) +{ + if (hasFocusWithin() == flag) + return; + setFlag(flag, HasFocusWithin); + if (styleAffectedByFocusWithin()) + invalidateStyleForSubtree(); +} } // namespace WebCore -#endif +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::Element) + static bool isType(const WebCore::Node& node) { return node.isElementNode(); } +SPECIALIZE_TYPE_TRAITS_END() diff --git a/Source/WebCore/dom/Element.idl b/Source/WebCore/dom/Element.idl index 6e7905419..4626e8925 100644 --- a/Source/WebCore/dom/Element.idl +++ b/Source/WebCore/dom/Element.idl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2009, 2015 Apple Inc. All rights reserved. * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> * * This library is free software; you can redistribute it and/or @@ -19,218 +19,168 @@ */ [ + CustomToJSObject, + JSCustomHeader, JSGenerateToNativeObject, + ExportMacro=WEBCORE_EXPORT, ] interface Element : Node { + readonly attribute DOMString? tagName; - // DOM Level 1 Core + [DOMJIT=ReadDOM] DOMString? getAttribute(DOMString name); - [TreatReturnedNullStringAs=Null] readonly attribute DOMString tagName; + [CEReactions, MayThrowException] void setAttribute(DOMString name, DOMString value); - [TreatReturnedNullStringAs=Null] DOMString getAttribute([Default=Undefined] optional DOMString name); - [ObjCLegacyUnnamedParameters, RaisesException] void setAttribute([Default=Undefined] optional DOMString name, - [Default=Undefined] optional DOMString value); - void removeAttribute([Default=Undefined] optional DOMString name); - Attr getAttributeNode([Default=Undefined] optional DOMString name); - [RaisesException] Attr setAttributeNode([Default=Undefined] optional Attr newAttr); - [RaisesException] Attr removeAttributeNode([Default=Undefined] optional Attr oldAttr); - NodeList getElementsByTagName([Default=Undefined] optional DOMString name); + [CEReactions] void removeAttribute(DOMString name); + [DOMJIT=ReadDOM] Attr? getAttributeNode(DOMString name); -#if !defined(LANGUAGE_OBJECTIVE_C) - // For ObjC this is defined on Node for legacy support. - readonly attribute NamedNodeMap attributes; - boolean hasAttributes(); -#endif + [CEReactions, MayThrowException] Attr? setAttributeNode(Attr newAttr); + [CEReactions, MayThrowException] Attr removeAttributeNode(Attr oldAttr); + + [DOMJIT=ReadDOM] HTMLCollection getElementsByTagName(DOMString name); + + readonly attribute NamedNodeMap attributes; + [DOMJIT=ReadDOM] boolean hasAttributes(); + + DOMString? getAttributeNS(DOMString? namespaceURI, DOMString localName); - // DOM Level 2 Core - - [ObjCLegacyUnnamedParameters] DOMString getAttributeNS([TreatNullAs=NullString,Default=Undefined] optional DOMString namespaceURI, - [Default=Undefined] optional DOMString localName); - [ObjCLegacyUnnamedParameters, RaisesException] void setAttributeNS([TreatNullAs=NullString,Default=Undefined] optional DOMString namespaceURI, - [Default=Undefined] optional DOMString qualifiedName, - [Default=Undefined] optional DOMString value); - [ObjCLegacyUnnamedParameters] void removeAttributeNS([TreatNullAs=NullString] DOMString namespaceURI, - DOMString localName); - [ObjCLegacyUnnamedParameters] NodeList getElementsByTagNameNS([TreatNullAs=NullString,Default=Undefined] optional DOMString namespaceURI, - [Default=Undefined] optional DOMString localName); - [ObjCLegacyUnnamedParameters] Attr getAttributeNodeNS([TreatNullAs=NullString,Default=Undefined] optional DOMString namespaceURI, - [Default=Undefined] optional DOMString localName); - [RaisesException] Attr setAttributeNodeNS([Default=Undefined] optional Attr newAttr); + [CEReactions, MayThrowException] void setAttributeNS(DOMString? namespaceURI, DOMString qualifiedName, DOMString value); + [CEReactions] void removeAttributeNS(DOMString? namespaceURI, DOMString localName); + + HTMLCollection getElementsByTagNameNS(DOMString? namespaceURI, DOMString localName); + + Attr? getAttributeNodeNS(DOMString? namespaceURI, DOMString localName); + [CEReactions, MayThrowException] Attr? setAttributeNodeNS(Attr newAttr); boolean hasAttribute(DOMString name); - [ObjCLegacyUnnamedParameters] boolean hasAttributeNS([TreatNullAs=NullString,Default=Undefined] optional DOMString namespaceURI, - [Default=Undefined] optional DOMString localName); - readonly attribute CSSStyleDeclaration style; + boolean hasAttributeNS(DOMString? namespaceURI, DOMString localName); - // DOM 4 -#if !defined(LANGUAGE_OBJECTIVE_C) || !LANGUAGE_OBJECTIVE_C - [Reflect] attribute DOMString id; -#endif + [ImplementedAs=cssomStyle] readonly attribute CSSStyleDeclaration style; - // Common extensions + [CEReactions, Reflect] attribute DOMString id; - readonly attribute long offsetLeft; - readonly attribute long offsetTop; - readonly attribute long offsetWidth; - readonly attribute long offsetHeight; - [ImplementedAs=bindingsOffsetParent] readonly attribute Element offsetParent; - readonly attribute long clientLeft; - readonly attribute long clientTop; - readonly attribute long clientWidth; - readonly attribute long clientHeight; - attribute long scrollLeft; - attribute long scrollTop; + readonly attribute DOMString? namespaceURI; + readonly attribute DOMString? prefix; + readonly attribute DOMString localName; + + readonly attribute double offsetLeft; + readonly attribute double offsetTop; + readonly attribute double offsetWidth; + readonly attribute double offsetHeight; + readonly attribute double clientLeft; + readonly attribute double clientTop; + readonly attribute double clientWidth; + readonly attribute double clientHeight; + + attribute long scrollLeft; // FIXME: should be unrestricted double + attribute long scrollTop; // FIXME: should be unrestricted double readonly attribute long scrollWidth; readonly attribute long scrollHeight; - void focus(); - void blur(); - void scrollIntoView(optional boolean alignWithTop); + [ImplementedAs=bindingsOffsetParent] readonly attribute Element offsetParent; - // WebKit extensions + void scrollIntoView(optional boolean alignWithTop = true); - void scrollIntoViewIfNeeded(optional boolean centerIfNeeded); - void scrollByLines([Default=Undefined] optional long lines); - void scrollByPages([Default=Undefined] optional long pages); + void scrollIntoViewIfNeeded(optional boolean centerIfNeeded = true); - // HTML 5 - NodeList getElementsByClassName([Default=Undefined] optional DOMString name); + [ImplementedAs=scrollTo] void scroll(optional ScrollToOptions options); + [ImplementedAs=scrollTo] void scroll(unrestricted double x, unrestricted double y); - [Reflect=class] attribute DOMString className; - readonly attribute DOMTokenList classList; + void scrollTo(optional ScrollToOptions options); + void scrollTo(unrestricted double x, unrestricted double y); -#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT - readonly attribute DOMStringMap dataset; -#endif + void scrollBy(optional ScrollToOptions option); + void scrollBy(unrestricted double x, unrestricted double y); - // NodeSelector - Selector API - [RaisesException] Element querySelector(DOMString selectors); - [RaisesException] NodeList querySelectorAll(DOMString selectors); - - // WebKit extension, pending specification. - [RaisesException] boolean webkitMatchesSelector([Default=Undefined] optional DOMString selectors); - - // ElementTraversal API - readonly attribute Element firstElementChild; - readonly attribute Element lastElementChild; - readonly attribute Element previousElementSibling; - readonly attribute Element nextElementSibling; - readonly attribute unsigned long childElementCount; - - // ShadowAware API -#if defined(ENABLE_SHADOW_DOM) && ENABLE_SHADOW_DOM && defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT - [Reflect=pseudo, ImplementedAs=pseudo] attribute DOMString webkitPseudo; - [ImplementedAs=createShadowRoot, RaisesException] ShadowRoot webkitCreateShadowRoot(); - [ImplementedAs=authorShadowRoot] readonly attribute ShadowRoot webkitShadowRoot; - [ImplementedAs=insertionParentForBinding] readonly attribute Node webkitInsertionParent; -#endif + void scrollByLines(optional long lines = 0); + void scrollByPages(optional long pages = 0); + + sequence<DOMString> getAttributeNames(); + + HTMLCollection getElementsByClassName(DOMString name); + [CEReactions, TreatNullAs=EmptyString, SetterMayThrowException] attribute DOMString innerHTML; + [CEReactions, TreatNullAs=EmptyString, SetterMayThrowException] attribute DOMString outerHTML; + + [CEReactions, Reflect=class] attribute DOMString className; + [PutForwards=value] readonly attribute DOMTokenList classList; + + [MayThrowException] boolean matches(DOMString selectors); + [MayThrowException] Element? closest(DOMString selectors); + [ImplementedAs=matches, MayThrowException] boolean webkitMatchesSelector(DOMString selectors); -#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT // CSSOM View Module API ClientRectList getClientRects(); ClientRect getBoundingClientRect(); -#endif - -#if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C - // Objective-C extensions - readonly attribute DOMString innerText; -#endif #if defined(ENABLE_FULLSCREEN_API) && ENABLE_FULLSCREEN_API // Mozilla version - const unsigned short ALLOW_KEYBOARD_INPUT = 1; - void webkitRequestFullScreen([Default=Undefined] optional unsigned short flags); + [ImplementedAs=webkitRequestFullscreen] void webkitRequestFullScreen(); // W3C version void webkitRequestFullscreen(); #endif -#if defined(ENABLE_INDIE_UI) && ENABLE_INDIE_UI - [Reflect] attribute DOMString uiactions; -#endif + [CEReactions, MayThrowException] Element? insertAdjacentElement(DOMString where, Element element); + [CEReactions, MayThrowException] void insertAdjacentHTML(DOMString where, DOMString html); + [MayThrowException] void insertAdjacentText(DOMString where, DOMString text); - [Conditional=POINTER_LOCK] void webkitRequestPointerLock(); + [Conditional=POINTER_LOCK] void requestPointerLock(); // CSS Regions API [Conditional=CSS_REGIONS] readonly attribute DOMString webkitRegionOverset; [Conditional=CSS_REGIONS] sequence<Range> webkitGetRegionFlowRanges(); -#if !defined(LANGUAGE_OBJECTIVE_C) || !LANGUAGE_OBJECTIVE_C - // Event handler DOM attributes - [NotEnumerable] attribute EventListener onabort; - [NotEnumerable] attribute EventListener onblur; - [NotEnumerable] attribute EventListener onchange; - [NotEnumerable] attribute EventListener onclick; - [NotEnumerable] attribute EventListener oncontextmenu; - [NotEnumerable] attribute EventListener ondblclick; - [NotEnumerable] attribute EventListener ondrag; - [NotEnumerable] attribute EventListener ondragend; - [NotEnumerable] attribute EventListener ondragenter; - [NotEnumerable] attribute EventListener ondragleave; - [NotEnumerable] attribute EventListener ondragover; - [NotEnumerable] attribute EventListener ondragstart; - [NotEnumerable] attribute EventListener ondrop; - [NotEnumerable] attribute EventListener onerror; - [NotEnumerable] attribute EventListener onfocus; - [NotEnumerable] attribute EventListener oninput; - [NotEnumerable] attribute EventListener oninvalid; - [NotEnumerable] attribute EventListener onkeydown; - [NotEnumerable] attribute EventListener onkeypress; - [NotEnumerable] attribute EventListener onkeyup; - [NotEnumerable] attribute EventListener onload; - [NotEnumerable] attribute EventListener onmousedown; - [NotEnumerable] attribute EventListener onmouseenter; - [NotEnumerable] attribute EventListener onmouseleave; - [NotEnumerable] attribute EventListener onmousemove; - [NotEnumerable] attribute EventListener onmouseout; - [NotEnumerable] attribute EventListener onmouseover; - [NotEnumerable] attribute EventListener onmouseup; - [NotEnumerable] attribute EventListener onmousewheel; - [NotEnumerable] attribute EventListener onscroll; - [NotEnumerable] attribute EventListener onselect; - [NotEnumerable] attribute EventListener onsubmit; - [NotEnumerable] attribute EventListener onwheel; - - // [NotEnumerable] attribute EventListener oncanplay; - // [NotEnumerable] attribute EventListener oncanplaythrough; - // [NotEnumerable] attribute EventListener ondurationchange; - // [NotEnumerable] attribute EventListener onemptied; - // [NotEnumerable] attribute EventListener onended; - // [NotEnumerable] attribute EventListener onloadeddata; - // [NotEnumerable] attribute EventListener onloadedmetadata; - // [NotEnumerable] attribute EventListener onloadstart; - // [NotEnumerable] attribute EventListener onpause; - // [NotEnumerable] attribute EventListener onplay; - // [NotEnumerable] attribute EventListener onplaying; - // [NotEnumerable] attribute EventListener onprogress; - // [NotEnumerable] attribute EventListener onratechange; - // [NotEnumerable] attribute EventListener onreadystatechange; - // [NotEnumerable] attribute EventListener onseeked; - // [NotEnumerable] attribute EventListener onseeking; - // [NotEnumerable] attribute EventListener onshow; - // [NotEnumerable] attribute EventListener onstalled; - // [NotEnumerable] attribute EventListener onsuspend; - // [NotEnumerable] attribute EventListener ontimeupdate; - // [NotEnumerable] attribute EventListener onvolumechange; - // [NotEnumerable] attribute EventListener onwaiting; - - // WebKit extensions - [NotEnumerable] attribute EventListener onbeforecut; - [NotEnumerable] attribute EventListener oncut; - [NotEnumerable] attribute EventListener onbeforecopy; - [NotEnumerable] attribute EventListener oncopy; - [NotEnumerable] attribute EventListener onbeforepaste; - [NotEnumerable] attribute EventListener onpaste; - [NotEnumerable] attribute EventListener onreset; - [NotEnumerable] attribute EventListener onsearch; - [NotEnumerable] attribute EventListener onselectstart; - [NotEnumerable,Conditional=TOUCH_EVENTS] attribute EventListener ontouchstart; - [NotEnumerable,Conditional=TOUCH_EVENTS] attribute EventListener ontouchmove; - [NotEnumerable,Conditional=TOUCH_EVENTS] attribute EventListener ontouchend; - [NotEnumerable,Conditional=TOUCH_EVENTS] attribute EventListener ontouchcancel; - [NotEnumerable, Conditional=FULLSCREEN_API] attribute EventListener onwebkitfullscreenchange; - [NotEnumerable, Conditional=FULLSCREEN_API] attribute EventListener onwebkitfullscreenerror; -#endif + // Shadow DOM API + [EnabledAtRuntime=ShadowDOM, MayThrowException] ShadowRoot attachShadow(ShadowRootInit init); + [EnabledAtRuntime=ShadowDOM, ImplementedAs=shadowRootForBindings, CallWith=ScriptState] readonly attribute ShadowRoot shadowRoot; + [CEReactions, EnabledAtRuntime=ShadowDOM, Reflect] attribute DOMString slot; + + // Event Handlers + + // Unique to Element and Document + // FIXME: Should these be exposed on Window as well (and therefore moved to GlobalEventHandlers.idl)? + [NotEnumerable] attribute EventHandler onbeforecopy; + [NotEnumerable] attribute EventHandler onbeforecut; + [NotEnumerable] attribute EventHandler onbeforeinput; + [NotEnumerable] attribute EventHandler onbeforepaste; + [NotEnumerable] attribute EventHandler oncopy; + [NotEnumerable] attribute EventHandler oncut; + [NotEnumerable] attribute EventHandler onpaste; + [NotEnumerable] attribute EventHandler onselectstart; + [NotEnumerable, Conditional=FULLSCREEN_API] attribute EventHandler onwebkitfullscreenchange; + [NotEnumerable, Conditional=FULLSCREEN_API] attribute EventHandler onwebkitfullscreenerror; + + // Unique to Element and DOMWindow + // FIXME: Should these be exposed on Document as well (and therefore moved to GlobalEventHandlers.idl)? + [NotEnumerable] attribute EventHandler onanimationend; + [NotEnumerable] attribute EventHandler onanimationiteration; + [NotEnumerable] attribute EventHandler onanimationstart; + [NotEnumerable] attribute EventHandler ontransitionend; + [NotEnumerable, ImplementedAs=onwebkitAnimationEnd] attribute EventHandler onwebkitanimationend; + [NotEnumerable, ImplementedAs=onwebkitAnimationIteration] attribute EventHandler onwebkitanimationiteration; + [NotEnumerable, ImplementedAs=onwebkitAnimationStart] attribute EventHandler onwebkitanimationstart; + [NotEnumerable, ImplementedAs=onwebkitTransitionEnd] attribute EventHandler onwebkittransitionend; + [NotEnumerable, Conditional=IOS_GESTURE_EVENTS] attribute EventHandler ongesturechange; + [NotEnumerable, Conditional=IOS_GESTURE_EVENTS] attribute EventHandler ongestureend; + [NotEnumerable, Conditional=IOS_GESTURE_EVENTS] attribute EventHandler ongesturestart; + + // Unique to Element, HTMLBodyElement and HTMLFrameSetElement + [NotEnumerable] attribute EventHandler onfocusin; + [NotEnumerable] attribute EventHandler onfocusout; + + // Unique to Element + [NotEnumerable] attribute EventHandler onbeforeload; + [NotEnumerable, Conditional=LEGACY_ENCRYPTED_MEDIA] attribute EventHandler onwebkitneedkey; + [NotEnumerable, Conditional=VIDEO_PRESENTATION_MODE] attribute EventHandler onwebkitpresentationmodechanged; + [NotEnumerable, Conditional=WIRELESS_PLAYBACK_TARGET] attribute EventHandler onwebkitcurrentplaybacktargetiswirelesschanged; + [NotEnumerable, Conditional=WIRELESS_PLAYBACK_TARGET] attribute EventHandler onwebkitplaybacktargetavailabilitychanged; }; -Element implements ChildNode; +dictionary ShadowRootInit { + required ShadowRootMode mode; +}; +Element implements Animatable; +Element implements ChildNode; +Element implements NonDocumentTypeChildNode; +Element implements ParentNode; +Element implements Slotable; diff --git a/Source/WebCore/dom/ElementAncestorIterator.h b/Source/WebCore/dom/ElementAncestorIterator.h index 6125cb8e0..4b62d38b3 100644 --- a/Source/WebCore/dom/ElementAncestorIterator.h +++ b/Source/WebCore/dom/ElementAncestorIterator.h @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ElementAncestorIterator_h -#define ElementAncestorIterator_h +#pragma once #include "ElementIterator.h" @@ -76,8 +75,8 @@ ElementAncestorIteratorAdapter<Element> elementAncestors(Element* descendant); ElementAncestorConstIteratorAdapter<Element> elementAncestors(const Element* descendant); template <typename ElementType> ElementAncestorIteratorAdapter<ElementType> lineageOfType(Element& first); template <typename ElementType> ElementAncestorConstIteratorAdapter<ElementType> lineageOfType(const Element& first); -template <typename ElementType> ElementAncestorIteratorAdapter<ElementType> ancestorsOfType(Element& descendant); -template <typename ElementType> ElementAncestorConstIteratorAdapter<ElementType> ancestorsOfType(const Element& descendant); +template <typename ElementType> ElementAncestorIteratorAdapter<ElementType> ancestorsOfType(Node& descendant); +template <typename ElementType> ElementAncestorConstIteratorAdapter<ElementType> ancestorsOfType(const Node& descendant); // ElementAncestorIterator @@ -184,7 +183,7 @@ inline ElementAncestorConstIteratorAdapter<Element> elementAncestors(const Eleme template <typename ElementType> inline ElementAncestorIteratorAdapter<ElementType> lineageOfType(Element& first) { - if (isElementOfType<const ElementType>(first)) + if (is<ElementType>(first)) return ElementAncestorIteratorAdapter<ElementType>(static_cast<ElementType*>(&first)); return ancestorsOfType<ElementType>(first); } @@ -192,25 +191,23 @@ inline ElementAncestorIteratorAdapter<ElementType> lineageOfType(Element& first) template <typename ElementType> inline ElementAncestorConstIteratorAdapter<ElementType> lineageOfType(const Element& first) { - if (isElementOfType<const ElementType>(first)) + if (is<ElementType>(first)) return ElementAncestorConstIteratorAdapter<ElementType>(static_cast<const ElementType*>(&first)); return ancestorsOfType<ElementType>(first); } template <typename ElementType> -inline ElementAncestorIteratorAdapter<ElementType> ancestorsOfType(Element& descendant) +inline ElementAncestorIteratorAdapter<ElementType> ancestorsOfType(Node& descendant) { ElementType* first = findElementAncestorOfType<ElementType>(descendant); return ElementAncestorIteratorAdapter<ElementType>(first); } template <typename ElementType> -inline ElementAncestorConstIteratorAdapter<ElementType> ancestorsOfType(const Element& descendant) +inline ElementAncestorConstIteratorAdapter<ElementType> ancestorsOfType(const Node& descendant) { const ElementType* first = findElementAncestorOfType<const ElementType>(descendant); return ElementAncestorConstIteratorAdapter<ElementType>(first); } -} - -#endif +} // namespace WebCore diff --git a/Source/WebCore/dom/ElementAndTextDescendantIterator.h b/Source/WebCore/dom/ElementAndTextDescendantIterator.h new file mode 100644 index 000000000..2c0c08dd4 --- /dev/null +++ b/Source/WebCore/dom/ElementAndTextDescendantIterator.h @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "Element.h" +#include "ElementIteratorAssertions.h" +#include "Text.h" +#include <wtf/Vector.h> + +namespace WebCore { + +class ElementAndTextDescendantIterator { +public: + ElementAndTextDescendantIterator(); + enum FirstChildTag { FirstChild }; + ElementAndTextDescendantIterator(ContainerNode& root, FirstChildTag); + ElementAndTextDescendantIterator(ContainerNode& root, Node* current); + + ElementAndTextDescendantIterator& operator++() { return traverseNext(); } + + Node& operator*(); + Node* operator->(); + const Node& operator*() const; + const Node* operator->() const; + + bool operator==(const ElementAndTextDescendantIterator& other) const; + bool operator!=(const ElementAndTextDescendantIterator& other) const; + + bool operator!() const { return !m_depth; } + explicit operator bool() const { return m_depth; } + + void dropAssertions(); + + ElementAndTextDescendantIterator& traverseNext(); + ElementAndTextDescendantIterator& traverseNextSkippingChildren(); + ElementAndTextDescendantIterator& traverseNextSibling(); + ElementAndTextDescendantIterator& traversePreviousSibling(); + + unsigned depth() const { return m_depth; } + +private: + static bool isElementOrText(const Node& node) { return is<Element>(node) || is<Text>(node); } + static Node* firstChild(Node&); + static Node* nextSibling(Node&); + static Node* previousSibling(Node&); + + void popAncestorSiblingStack(); + + Node* m_current; + struct AncestorSibling { + Node* node; + unsigned depth; + }; + Vector<AncestorSibling, 16> m_ancestorSiblingStack; + unsigned m_depth { 0 }; + +#if !ASSERT_DISABLED + ElementIteratorAssertions m_assertions; +#endif +}; + +class ElementAndTextDescendantIteratorAdapter { +public: + explicit ElementAndTextDescendantIteratorAdapter(ContainerNode& root); + ElementAndTextDescendantIterator begin(); + ElementAndTextDescendantIterator end(); + +private: + ContainerNode& m_root; +}; + +ElementAndTextDescendantIteratorAdapter elementAndTextDescendants(ContainerNode&); + +// ElementAndTextDescendantIterator + +inline ElementAndTextDescendantIterator::ElementAndTextDescendantIterator() + : m_current(nullptr) +{ +} + +inline ElementAndTextDescendantIterator::ElementAndTextDescendantIterator(ContainerNode& root, FirstChildTag) + : m_current(firstChild(root)) +#if !ASSERT_DISABLED + , m_assertions(m_current) +#endif +{ + if (!m_current) + return; + m_ancestorSiblingStack.uncheckedAppend({ nullptr, 0 }); + m_depth = 1; +} + +inline ElementAndTextDescendantIterator::ElementAndTextDescendantIterator(ContainerNode& root, Node* current) + : m_current(current) +#if !ASSERT_DISABLED + , m_assertions(m_current) +#endif +{ + if (!m_current) + return; + ASSERT(isElementOrText(*m_current)); + if (m_current == &root) + return; + + Vector<Node*, 20> ancestorStack; + auto* ancestor = m_current->parentNode(); + while (ancestor != &root) { + ancestorStack.append(ancestor); + ancestor = ancestor->parentNode(); + } + + m_ancestorSiblingStack.uncheckedAppend({ nullptr, 0 }); + for (unsigned i = ancestorStack.size(); i; --i) { + if (auto* sibling = nextSibling(*ancestorStack[i - 1])) + m_ancestorSiblingStack.append({ sibling, i }); + } + + m_depth = ancestorStack.size() + 1; +} + +inline void ElementAndTextDescendantIterator::dropAssertions() +{ +#if !ASSERT_DISABLED + m_assertions.clear(); +#endif +} + +inline Node* ElementAndTextDescendantIterator::firstChild(Node& current) +{ + auto* node = current.firstChild(); + while (node && !isElementOrText(*node)) + node = node->nextSibling(); + return node; +} + +inline Node* ElementAndTextDescendantIterator::nextSibling(Node& current) +{ + auto* node = current.nextSibling(); + while (node && !isElementOrText(*node)) + node = node->nextSibling(); + return node; +} + +inline Node* ElementAndTextDescendantIterator::previousSibling(Node& current) +{ + auto* node = current.previousSibling(); + while (node && !isElementOrText(*node)) + node = node->previousSibling(); + return node; +} + +inline void ElementAndTextDescendantIterator::popAncestorSiblingStack() +{ + m_current = m_ancestorSiblingStack.last().node; + m_depth = m_ancestorSiblingStack.last().depth; + m_ancestorSiblingStack.removeLast(); + +#if !ASSERT_DISABLED + // Drop the assertion when the iterator reaches the end. + if (!m_current) + m_assertions.dropEventDispatchAssertion(); +#endif +} + +inline ElementAndTextDescendantIterator& ElementAndTextDescendantIterator::traverseNext() +{ + ASSERT(m_current); + ASSERT(!m_assertions.domTreeHasMutated()); + + auto* firstChild = ElementAndTextDescendantIterator::firstChild(*m_current); + auto* nextSibling = ElementAndTextDescendantIterator::nextSibling(*m_current); + if (firstChild) { + if (nextSibling) + m_ancestorSiblingStack.append({ nextSibling, m_depth }); + ++m_depth; + m_current = firstChild; + return *this; + } + if (!nextSibling) { + popAncestorSiblingStack(); + return *this; + } + + m_current = nextSibling; + return *this; +} + +inline ElementAndTextDescendantIterator& ElementAndTextDescendantIterator::traverseNextSkippingChildren() +{ + ASSERT(m_current); + ASSERT(!m_assertions.domTreeHasMutated()); + + auto* nextSibling = ElementAndTextDescendantIterator::nextSibling(*m_current); + if (!nextSibling) { + popAncestorSiblingStack(); + return *this; + } + + m_current = nextSibling; + return *this; +} + +inline ElementAndTextDescendantIterator& ElementAndTextDescendantIterator::traverseNextSibling() +{ + ASSERT(m_current); + ASSERT(!m_assertions.domTreeHasMutated()); + + m_current = nextSibling(*m_current); + +#if !ASSERT_DISABLED + if (!m_current) + m_assertions.dropEventDispatchAssertion(); +#endif + return *this; +} + +inline ElementAndTextDescendantIterator& ElementAndTextDescendantIterator::traversePreviousSibling() +{ + ASSERT(m_current); + ASSERT(!m_assertions.domTreeHasMutated()); + + m_current = previousSibling(*m_current); + +#if !ASSERT_DISABLED + if (!m_current) + m_assertions.dropEventDispatchAssertion(); +#endif + return *this; +} + +inline Node& ElementAndTextDescendantIterator::operator*() +{ + ASSERT(m_current); + ASSERT(isElementOrText(*m_current)); + ASSERT(!m_assertions.domTreeHasMutated()); + return *m_current; +} + +inline Node* ElementAndTextDescendantIterator::operator->() +{ + ASSERT(m_current); + ASSERT(isElementOrText(*m_current)); + ASSERT(!m_assertions.domTreeHasMutated()); + return m_current; +} + +inline const Node& ElementAndTextDescendantIterator::operator*() const +{ + ASSERT(m_current); + ASSERT(isElementOrText(*m_current)); + ASSERT(!m_assertions.domTreeHasMutated()); + return *m_current; +} + +inline const Node* ElementAndTextDescendantIterator::operator->() const +{ + ASSERT(m_current); + ASSERT(isElementOrText(*m_current)); + ASSERT(!m_assertions.domTreeHasMutated()); + return m_current; +} + +inline bool ElementAndTextDescendantIterator::operator==(const ElementAndTextDescendantIterator& other) const +{ + ASSERT(!m_assertions.domTreeHasMutated()); + return m_current == other.m_current || (!m_depth && !other.m_depth); +} + +inline bool ElementAndTextDescendantIterator::operator!=(const ElementAndTextDescendantIterator& other) const +{ + return !(*this == other); +} + +// ElementAndTextDescendantIteratorAdapter + +inline ElementAndTextDescendantIteratorAdapter::ElementAndTextDescendantIteratorAdapter(ContainerNode& root) + : m_root(root) +{ +} + +inline ElementAndTextDescendantIterator ElementAndTextDescendantIteratorAdapter::begin() +{ + return ElementAndTextDescendantIterator(m_root, ElementAndTextDescendantIterator::FirstChild); +} + +inline ElementAndTextDescendantIterator ElementAndTextDescendantIteratorAdapter::end() +{ + return { }; +} + +// Standalone functions + +inline ElementAndTextDescendantIteratorAdapter elementAndTextDescendants(ContainerNode& root) +{ + return ElementAndTextDescendantIteratorAdapter(root); +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/ElementChildIterator.h b/Source/WebCore/dom/ElementChildIterator.h index 170af861c..8e38fb503 100644 --- a/Source/WebCore/dom/ElementChildIterator.h +++ b/Source/WebCore/dom/ElementChildIterator.h @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ElementChildIterator_h -#define ElementChildIterator_h +#pragma once #include "ElementIterator.h" @@ -33,16 +32,30 @@ namespace WebCore { template <typename ElementType> class ElementChildIterator : public ElementIterator<ElementType> { public: + typedef ElementType value_type; + typedef ptrdiff_t difference_type; + typedef ElementType* pointer; + typedef ElementType& reference; + typedef std::forward_iterator_tag iterator_category; + ElementChildIterator(const ContainerNode& parent); ElementChildIterator(const ContainerNode& parent, ElementType* current); + ElementChildIterator& operator--(); ElementChildIterator& operator++(); }; template <typename ElementType> class ElementChildConstIterator : public ElementConstIterator<ElementType> { public: + typedef const ElementType value_type; + typedef ptrdiff_t difference_type; + typedef const ElementType* pointer; + typedef const ElementType& reference; + typedef std::forward_iterator_tag iterator_category; + ElementChildConstIterator(const ContainerNode& parent); ElementChildConstIterator(const ContainerNode& parent, const ElementType* current); + ElementChildConstIterator& operator--(); ElementChildConstIterator& operator++(); }; @@ -96,6 +109,12 @@ inline ElementChildIterator<ElementType>::ElementChildIterator(const ContainerNo } template <typename ElementType> +inline ElementChildIterator<ElementType>& ElementChildIterator<ElementType>::operator--() +{ + return static_cast<ElementChildIterator<ElementType>&>(ElementIterator<ElementType>::traversePreviousSibling()); +} + +template <typename ElementType> inline ElementChildIterator<ElementType>& ElementChildIterator<ElementType>::operator++() { return static_cast<ElementChildIterator<ElementType>&>(ElementIterator<ElementType>::traverseNextSibling()); @@ -116,6 +135,13 @@ inline ElementChildConstIterator<ElementType>::ElementChildConstIterator(const C } template <typename ElementType> +inline ElementChildConstIterator<ElementType>& ElementChildConstIterator<ElementType>::operator--() +{ + return static_cast<ElementChildConstIterator<ElementType>&>(ElementConstIterator<ElementType>::traversePreviousSibling()); +} + + +template <typename ElementType> inline ElementChildConstIterator<ElementType>& ElementChildConstIterator<ElementType>::operator++() { return static_cast<ElementChildConstIterator<ElementType>&>(ElementConstIterator<ElementType>::traverseNextSibling()); @@ -132,7 +158,7 @@ inline ElementChildIteratorAdapter<ElementType>::ElementChildIteratorAdapter(Con template <typename ElementType> inline ElementChildIterator<ElementType> ElementChildIteratorAdapter<ElementType>::begin() { - return ElementChildIterator<ElementType>(m_parent, Traversal<ElementType>::firstChild(&m_parent)); + return ElementChildIterator<ElementType>(m_parent, Traversal<ElementType>::firstChild(m_parent)); } template <typename ElementType> @@ -144,13 +170,13 @@ inline ElementChildIterator<ElementType> ElementChildIteratorAdapter<ElementType template <typename ElementType> inline ElementType* ElementChildIteratorAdapter<ElementType>::first() { - return Traversal<ElementType>::firstChild(&m_parent); + return Traversal<ElementType>::firstChild(m_parent); } template <typename ElementType> inline ElementType* ElementChildIteratorAdapter<ElementType>::last() { - return Traversal<ElementType>::lastChild(&m_parent); + return Traversal<ElementType>::lastChild(m_parent); } template <typename ElementType> @@ -171,7 +197,7 @@ inline ElementChildConstIteratorAdapter<ElementType>::ElementChildConstIteratorA template <typename ElementType> inline ElementChildConstIterator<ElementType> ElementChildConstIteratorAdapter<ElementType>::begin() const { - return ElementChildConstIterator<ElementType>(m_parent, Traversal<ElementType>::firstChild(&m_parent)); + return ElementChildConstIterator<ElementType>(m_parent, Traversal<ElementType>::firstChild(m_parent)); } template <typename ElementType> @@ -183,13 +209,13 @@ inline ElementChildConstIterator<ElementType> ElementChildConstIteratorAdapter<E template <typename ElementType> inline const ElementType* ElementChildConstIteratorAdapter<ElementType>::first() const { - return Traversal<ElementType>::firstChild(&m_parent); + return Traversal<ElementType>::firstChild(m_parent); } template <typename ElementType> inline const ElementType* ElementChildConstIteratorAdapter<ElementType>::last() const { - return Traversal<ElementType>::lastChild(&m_parent); + return Traversal<ElementType>::lastChild(m_parent); } template <typename ElementType> @@ -213,6 +239,4 @@ inline ElementChildConstIteratorAdapter<ElementType> childrenOfType(const Contai return ElementChildConstIteratorAdapter<ElementType>(parent); } -} - -#endif +} // namespace WebCore diff --git a/Source/WebCore/dom/ElementData.cpp b/Source/WebCore/dom/ElementData.cpp index 86f852d05..4b9ebf889 100644 --- a/Source/WebCore/dom/ElementData.cpp +++ b/Source/WebCore/dom/ElementData.cpp @@ -27,16 +27,18 @@ #include "ElementData.h" #include "Attr.h" +#include "HTMLNames.h" #include "StyleProperties.h" +#include "XMLNames.h" namespace WebCore { void ElementData::destroy() { - if (isUnique()) - delete static_cast<UniqueElementData*>(this); + if (is<UniqueElementData>(*this)) + delete downcast<UniqueElementData>(this); else - delete static_cast<ShareableElementData*>(this); + delete downcast<ShareableElementData>(this); } ElementData::ElementData() @@ -61,13 +63,13 @@ static size_t sizeForShareableElementDataWithAttributeCount(unsigned count) return sizeof(ShareableElementData) + sizeof(Attribute) * count; } -PassRef<ShareableElementData> ShareableElementData::createWithAttributes(const Vector<Attribute>& attributes) +Ref<ShareableElementData> ShareableElementData::createWithAttributes(const Vector<Attribute>& attributes) { void* slot = WTF::fastMalloc(sizeForShareableElementDataWithAttributeCount(attributes.size())); return adoptRef(*new (NotNull, slot) ShareableElementData(attributes)); } -PassRef<UniqueElementData> UniqueElementData::create() +Ref<UniqueElementData> UniqueElementData::create() { return adoptRef(*new UniqueElementData); } @@ -140,19 +142,20 @@ UniqueElementData::UniqueElementData(const ShareableElementData& other) ASSERT(!other.m_inlineStyle || !other.m_inlineStyle->isMutable()); m_inlineStyle = other.m_inlineStyle; - m_attributeVector.reserveCapacity(other.length()); - for (unsigned i = 0; i < other.length(); ++i) + unsigned otherLength = other.length(); + m_attributeVector.reserveCapacity(otherLength); + for (unsigned i = 0; i < otherLength; ++i) m_attributeVector.uncheckedAppend(other.m_attributeArray[i]); } -PassRef<UniqueElementData> ElementData::makeUniqueCopy() const +Ref<UniqueElementData> ElementData::makeUniqueCopy() const { if (isUnique()) return adoptRef(*new UniqueElementData(static_cast<const UniqueElementData&>(*this))); return adoptRef(*new UniqueElementData(static_cast<const ShareableElementData&>(*this))); } -PassRef<ShareableElementData> UniqueElementData::makeShareableCopy() const +Ref<ShareableElementData> UniqueElementData::makeShareableCopy() const { void* slot = WTF::fastMalloc(sizeForShareableElementDataWithAttributeCount(m_attributeVector.size())); return adoptRef(*new (NotNull, slot) ShareableElementData(*this)); @@ -175,46 +178,32 @@ bool ElementData::isEquivalent(const ElementData* other) const return true; } -unsigned ElementData::findAttributeIndexByNameSlowCase(const AtomicString& name, bool shouldIgnoreAttributeCase) const +Attribute* UniqueElementData::findAttributeByName(const QualifiedName& name) { - // Continue to checking case-insensitively and/or full namespaced names if necessary: - const Attribute* attributes = attributeBase(); - unsigned length = this->length(); - for (unsigned i = 0; i < length; ++i) { - const Attribute& attribute = attributes[i]; - if (!attribute.name().hasPrefix()) { - if (shouldIgnoreAttributeCase && equalIgnoringCase(name, attribute.localName())) - return i; - } else { - // FIXME: Would be faster to do this comparison without calling toString, which - // generates a temporary string by concatenation. But this branch is only reached - // if the attribute name has a prefix, which is rare in HTML. - if (equalPossiblyIgnoringCase(name, attribute.name().toString(), shouldIgnoreAttributeCase)) - return i; - } + for (auto& attribute : m_attributeVector) { + if (attribute.name().matches(name)) + return &attribute; } - return attributeNotFound; + return nullptr; } -unsigned ElementData::findAttributeIndexByNameForAttributeNode(const Attr* attr, bool shouldIgnoreAttributeCase) const +const Attribute* ElementData::findLanguageAttribute() const { - ASSERT(attr); - const Attribute* attributes = attributeBase(); - unsigned count = length(); - for (unsigned i = 0; i < count; ++i) { - if (attributes[i].name().matchesIgnoringCaseForLocalName(attr->qualifiedName(), shouldIgnoreAttributeCase)) - return i; - } - return attributeNotFound; -} + ASSERT(XMLNames::langAttr.localName() == HTMLNames::langAttr.localName()); -Attribute* UniqueElementData::findAttributeByName(const QualifiedName& name) -{ - for (unsigned i = 0, count = m_attributeVector.size(); i < count; ++i) { - if (m_attributeVector.at(i).name().matches(name)) - return &m_attributeVector.at(i); + const Attribute* attributes = attributeBase(); + // Spec: xml:lang takes precedence over html:lang -- http://www.w3.org/TR/xhtml1/#C_7 + const Attribute* languageAttribute = nullptr; + for (unsigned i = 0, count = length(); i < count; ++i) { + const QualifiedName& name = attributes[i].name(); + if (name.localName() != HTMLNames::langAttr.localName()) + continue; + if (name.namespaceURI() == XMLNames::langAttr.namespaceURI()) + return &attributes[i]; + if (name.namespaceURI() == HTMLNames::langAttr.namespaceURI()) + languageAttribute = &attributes[i]; } - return nullptr; + return languageAttribute; } } diff --git a/Source/WebCore/dom/ElementData.h b/Source/WebCore/dom/ElementData.h index 296b37c49..f6d1eafd7 100644 --- a/Source/WebCore/dom/ElementData.h +++ b/Source/WebCore/dom/ElementData.h @@ -23,12 +23,12 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ElementData_h -#define ElementData_h +#pragma once #include "Attribute.h" #include "SpaceSplitString.h" #include <wtf/RefCounted.h> +#include <wtf/TypeCasts.h> namespace WebCore { @@ -67,6 +67,9 @@ public: AttributeConstIterator begin() const { return AttributeConstIterator(m_array, 0); } AttributeConstIterator end() const { return AttributeConstIterator(m_array, m_size); } + + unsigned attributeCount() const { return m_size; } + private: const Attribute* m_array; unsigned m_size; @@ -81,8 +84,7 @@ public: static const unsigned attributeNotFound = static_cast<unsigned>(-1); - void clearClass() const { m_classNames.clear(); } - void setClass(const AtomicString& className, bool shouldFoldCase) const { m_classNames.set(className, shouldFoldCase); } + void setClassNames(const SpaceSplitString& classNames) const { m_classNames = classNames; } const SpaceSplitString& classNames() const { return m_classNames; } static ptrdiff_t classNamesMemoryOffset() { return OBJECT_OFFSETOF(ElementData, m_classNames); } @@ -101,7 +103,7 @@ public: const Attribute* findAttributeByName(const QualifiedName&) const; unsigned findAttributeIndexByName(const QualifiedName&) const; unsigned findAttributeIndexByName(const AtomicString& name, bool shouldIgnoreAttributeCase) const; - unsigned findAttributeIndexByNameForAttributeNode(const Attr*, bool shouldIgnoreAttributeCase = false) const; + const Attribute* findLanguageAttribute() const; bool hasID() const { return !m_idForStyleResolution.isNull(); } bool hasClass() const { return !m_classNames.isEmpty(); } @@ -110,6 +112,13 @@ public: bool isEquivalent(const ElementData* other) const; bool isUnique() const { return m_arraySizeAndFlags & s_flagIsUnique; } + static uint32_t isUniqueFlag() { return s_flagIsUnique; } + + static ptrdiff_t arraySizeAndFlagsMemoryOffset() { return OBJECT_OFFSETOF(ElementData, m_arraySizeAndFlags); } + static inline uint32_t styleAttributeIsDirtyFlag() { return s_flagStyleAttributeIsDirty; } + static uint32_t animatedSVGAttributesAreDirtyFlag() { return s_flagAnimatedSVGAttributesAreDirty; } + + static uint32_t arraySizeOffset() { return s_flagCount; } private: mutable uint32_t m_arraySizeAndFlags; @@ -120,9 +129,7 @@ private: static const uint32_t s_flagHasNameAttribute = 1 << 1; static const uint32_t s_flagPresentationAttributeStyleIsDirty = 1 << 2; static const uint32_t s_flagStyleAttributeIsDirty = 1 << 3; -#if ENABLE(SVG) static const uint32_t s_flagAnimatedSVGAttributesAreDirty = 1 << 4; -#endif static const uint32_t s_flagsMask = (1 << s_flagCount) - 1; inline void updateFlag(uint32_t flag, bool set) const @@ -149,10 +156,8 @@ protected: bool presentationAttributeStyleIsDirty() const { return m_arraySizeAndFlags & s_flagPresentationAttributeStyleIsDirty; } void setPresentationAttributeStyleIsDirty(bool isDirty) const { updateFlag(s_flagPresentationAttributeStyleIsDirty, isDirty); } -#if ENABLE(SVG) bool animatedSVGAttributesAreDirty() const { return m_arraySizeAndFlags & s_flagAnimatedSVGAttributesAreDirty; } void setAnimatedSVGAttributesAreDirty(bool dirty) const { updateFlag(s_flagAnimatedSVGAttributesAreDirty, dirty); } -#endif mutable RefPtr<StyleProperties> m_inlineStyle; mutable SpaceSplitString m_classNames; @@ -163,17 +168,14 @@ private: friend class StyledElement; friend class ShareableElementData; friend class UniqueElementData; -#if ENABLE(SVG) friend class SVGElement; -#endif void destroy(); const Attribute* attributeBase() const; const Attribute* findAttributeByName(const AtomicString& name, bool shouldIgnoreAttributeCase) const; - unsigned findAttributeIndexByNameSlowCase(const AtomicString& name, bool shouldIgnoreAttributeCase) const; - PassRef<UniqueElementData> makeUniqueCopy() const; + Ref<UniqueElementData> makeUniqueCopy() const; }; #if COMPILER(MSVC) @@ -183,12 +185,14 @@ private: class ShareableElementData : public ElementData { public: - static PassRef<ShareableElementData> createWithAttributes(const Vector<Attribute>&); + static Ref<ShareableElementData> createWithAttributes(const Vector<Attribute>&); explicit ShareableElementData(const Vector<Attribute>&); explicit ShareableElementData(const UniqueElementData&); ~ShareableElementData(); + static ptrdiff_t attributeArrayMemoryOffset() { return OBJECT_OFFSETOF(ShareableElementData, m_attributeArray); } + Attribute m_attributeArray[0]; }; @@ -198,8 +202,8 @@ public: class UniqueElementData : public ElementData { public: - static PassRef<UniqueElementData> create(); - PassRef<ShareableElementData> makeShareableCopy() const; + static Ref<UniqueElementData> create(); + Ref<ShareableElementData> makeShareableCopy() const; // These functions do no error/duplicate checking. void addAttribute(const QualifiedName&, const AtomicString&); @@ -212,8 +216,11 @@ public: explicit UniqueElementData(const ShareableElementData&); explicit UniqueElementData(const UniqueElementData&); + static ptrdiff_t attributeVectorMemoryOffset() { return OBJECT_OFFSETOF(UniqueElementData, m_attributeVector); } + mutable RefPtr<StyleProperties> m_presentationAttributeStyle; - Vector<Attribute, 4> m_attributeVector; + typedef Vector<Attribute, 4> AttributeVector; + AttributeVector m_attributeVector; }; inline void ElementData::deref() @@ -225,43 +232,43 @@ inline void ElementData::deref() inline unsigned ElementData::length() const { - if (isUnique()) - return static_cast<const UniqueElementData*>(this)->m_attributeVector.size(); + if (is<UniqueElementData>(*this)) + return downcast<UniqueElementData>(*this).m_attributeVector.size(); return arraySize(); } inline const Attribute* ElementData::attributeBase() const { - if (isUnique()) - return static_cast<const UniqueElementData*>(this)->m_attributeVector.data(); - return static_cast<const ShareableElementData*>(this)->m_attributeArray; + if (is<UniqueElementData>(*this)) + return downcast<UniqueElementData>(*this).m_attributeVector.data(); + return downcast<ShareableElementData>(*this).m_attributeArray; } inline const StyleProperties* ElementData::presentationAttributeStyle() const { - if (!isUnique()) - return 0; - return static_cast<const UniqueElementData*>(this)->m_presentationAttributeStyle.get(); + if (!is<UniqueElementData>(*this)) + return nullptr; + return downcast<UniqueElementData>(*this).m_presentationAttributeStyle.get(); } inline AttributeIteratorAccessor ElementData::attributesIterator() const { - if (isUnique()) { - const Vector<Attribute, 4>& attributeVector = static_cast<const UniqueElementData*>(this)->m_attributeVector; + if (is<UniqueElementData>(*this)) { + const Vector<Attribute, 4>& attributeVector = downcast<UniqueElementData>(*this).m_attributeVector; return AttributeIteratorAccessor(attributeVector.data(), attributeVector.size()); } - return AttributeIteratorAccessor(static_cast<const ShareableElementData*>(this)->m_attributeArray, arraySize()); + return AttributeIteratorAccessor(downcast<ShareableElementData>(*this).m_attributeArray, arraySize()); } -inline const Attribute* ElementData::findAttributeByName(const AtomicString& name, bool shouldIgnoreAttributeCase) const +ALWAYS_INLINE const Attribute* ElementData::findAttributeByName(const AtomicString& name, bool shouldIgnoreAttributeCase) const { unsigned index = findAttributeIndexByName(name, shouldIgnoreAttributeCase); if (index != attributeNotFound) return &attributeAt(index); - return 0; + return nullptr; } -inline unsigned ElementData::findAttributeIndexByName(const QualifiedName& name) const +ALWAYS_INLINE unsigned ElementData::findAttributeIndexByName(const QualifiedName& name) const { const Attribute* attributes = attributeBase(); for (unsigned i = 0, count = length(); i < count; ++i) { @@ -273,27 +280,33 @@ inline unsigned ElementData::findAttributeIndexByName(const QualifiedName& name) // We use a boolean parameter instead of calling shouldIgnoreAttributeCase so that the caller // can tune the behavior (hasAttribute is case sensitive whereas getAttribute is not). -inline unsigned ElementData::findAttributeIndexByName(const AtomicString& name, bool shouldIgnoreAttributeCase) const +ALWAYS_INLINE unsigned ElementData::findAttributeIndexByName(const AtomicString& name, bool shouldIgnoreAttributeCase) const { - const Attribute* attributes = attributeBase(); - bool doSlowCheck = shouldIgnoreAttributeCase; - const AtomicString& caseAdjustedName = shouldIgnoreAttributeCase ? name.lower() : name; + unsigned attributeCount = length(); + if (!attributeCount) + return attributeNotFound; - // Optimize for the case where the attribute exists and its name exactly matches. - for (unsigned i = 0, count = length(); i < count; ++i) { - if (!attributes[i].name().hasPrefix()) { - if (caseAdjustedName == attributes[i].localName()) - return i; - } else - doSlowCheck = true; - } + const Attribute* attributes = attributeBase(); + const AtomicString& caseAdjustedName = shouldIgnoreAttributeCase ? name.convertToASCIILowercase() : name; + + unsigned attributeIndex = 0; + do { + const Attribute& attribute = attributes[attributeIndex]; + if (!attribute.name().hasPrefix()) { + if (attribute.localName() == caseAdjustedName) + return attributeIndex; + } else { + if (attribute.name().toString() == caseAdjustedName) + return attributeIndex; + } + + ++attributeIndex; + } while (attributeIndex < attributeCount); - if (doSlowCheck) - return findAttributeIndexByNameSlowCase(name, shouldIgnoreAttributeCase); return attributeNotFound; } -inline const Attribute* ElementData::findAttributeByName(const QualifiedName& name) const +ALWAYS_INLINE const Attribute* ElementData::findAttributeByName(const QualifiedName& name) const { const Attribute* attributes = attributeBase(); for (unsigned i = 0, count = length(); i < count; ++i) { @@ -324,7 +337,12 @@ inline Attribute& UniqueElementData::attributeAt(unsigned index) return m_attributeVector.at(index); } -} +} // namespace WebCore -#endif +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ShareableElementData) + static bool isType(const WebCore::ElementData& elementData) { return !elementData.isUnique(); } +SPECIALIZE_TYPE_TRAITS_END() +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::UniqueElementData) + static bool isType(const WebCore::ElementData& elementData) { return elementData.isUnique(); } +SPECIALIZE_TYPE_TRAITS_END() diff --git a/Source/WebCore/dom/ElementDescendantIterator.h b/Source/WebCore/dom/ElementDescendantIterator.h index 8cb3b3825..6ba171db5 100644 --- a/Source/WebCore/dom/ElementDescendantIterator.h +++ b/Source/WebCore/dom/ElementDescendantIterator.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Apple Inc. All rights reserved. + * Copyright (C) 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,217 +23,344 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ElementDescendantIterator_h -#define ElementDescendantIterator_h +#pragma once -#include "ElementIterator.h" +#include "Element.h" +#include "ElementIteratorAssertions.h" +#include "ElementTraversal.h" +#include <wtf/Vector.h> namespace WebCore { -template <typename ElementType> -class ElementDescendantIterator : public ElementIterator<ElementType> { +class ElementDescendantIterator { public: - ElementDescendantIterator(const ContainerNode& root); - ElementDescendantIterator(const ContainerNode& root, ElementType* current); + ElementDescendantIterator(); + explicit ElementDescendantIterator(Element* current); + ElementDescendantIterator& operator++(); + ElementDescendantIterator& operator--(); + + Element& operator*(); + Element* operator->(); + + bool operator==(const ElementDescendantIterator& other) const; + bool operator!=(const ElementDescendantIterator& other) const; + + void dropAssertions(); + +private: + Element* m_current; + Vector<Element*, 16> m_ancestorSiblingStack; + +#if !ASSERT_DISABLED + ElementIteratorAssertions m_assertions; +#endif }; -template <typename ElementType> -class ElementDescendantConstIterator : public ElementConstIterator<ElementType> { +class ElementDescendantConstIterator { public: - ElementDescendantConstIterator(const ContainerNode& root); - ElementDescendantConstIterator(const ContainerNode& root, const ElementType* current); + ElementDescendantConstIterator(); + explicit ElementDescendantConstIterator(const Element*); + ElementDescendantConstIterator& operator++(); + + const Element& operator*() const; + const Element* operator->() const; + + bool operator==(const ElementDescendantConstIterator& other) const; + bool operator!=(const ElementDescendantConstIterator& other) const; + + void dropAssertions(); + +private: + const Element* m_current; + Vector<Element*, 16> m_ancestorSiblingStack; + +#if !ASSERT_DISABLED + ElementIteratorAssertions m_assertions; +#endif }; -template <typename ElementType> class ElementDescendantIteratorAdapter { public: ElementDescendantIteratorAdapter(ContainerNode& root); - ElementDescendantIterator<ElementType> begin(); - ElementDescendantIterator<ElementType> end(); - ElementDescendantIterator<ElementType> beginAt(ElementType&); - ElementDescendantIterator<ElementType> from(Element&); - - ElementType* first(); - ElementType* last(); + ElementDescendantIterator begin(); + ElementDescendantIterator end(); + ElementDescendantIterator last(); private: ContainerNode& m_root; }; -template <typename ElementType> class ElementDescendantConstIteratorAdapter { public: ElementDescendantConstIteratorAdapter(const ContainerNode& root); - ElementDescendantConstIterator<ElementType> begin() const; - ElementDescendantConstIterator<ElementType> end() const; - ElementDescendantConstIterator<ElementType> beginAt(const ElementType&) const; - ElementDescendantConstIterator<ElementType> from(const Element&) const; - - const ElementType* first() const; - const ElementType* last() const; + ElementDescendantConstIterator begin() const; + ElementDescendantConstIterator end() const; + ElementDescendantConstIterator last() const; private: const ContainerNode& m_root; }; -template <typename ElementType> ElementDescendantIteratorAdapter<ElementType> descendantsOfType(ContainerNode&); -template <typename ElementType> ElementDescendantConstIteratorAdapter<ElementType> descendantsOfType(const ContainerNode&); +ElementDescendantIteratorAdapter elementDescendants(ContainerNode&); +ElementDescendantConstIteratorAdapter elementDescendants(const ContainerNode&); // ElementDescendantIterator -template <typename ElementType> -inline ElementDescendantIterator<ElementType>::ElementDescendantIterator(const ContainerNode& root) - : ElementIterator<ElementType>(&root) +inline ElementDescendantIterator::ElementDescendantIterator() + : m_current(nullptr) { } -template <typename ElementType> -inline ElementDescendantIterator<ElementType>::ElementDescendantIterator(const ContainerNode& root, ElementType* current) - : ElementIterator<ElementType>(&root, current) +inline ElementDescendantIterator::ElementDescendantIterator(Element* current) + : m_current(current) +#if !ASSERT_DISABLED + , m_assertions(current) +#endif { + m_ancestorSiblingStack.uncheckedAppend(nullptr); } -template <typename ElementType> -inline ElementDescendantIterator<ElementType>& ElementDescendantIterator<ElementType>::operator++() +inline void ElementDescendantIterator::dropAssertions() { - return static_cast<ElementDescendantIterator<ElementType>&>(ElementIterator<ElementType>::traverseNext()); +#if !ASSERT_DISABLED + m_assertions.clear(); +#endif } -// ElementDescendantConstIterator +ALWAYS_INLINE ElementDescendantIterator& ElementDescendantIterator::operator++() +{ + ASSERT(m_current); + ASSERT(!m_assertions.domTreeHasMutated()); + + Element* firstChild = ElementTraversal::firstChild(*m_current); + Element* nextSibling = ElementTraversal::nextSibling(*m_current); + + if (firstChild) { + if (nextSibling) + m_ancestorSiblingStack.append(nextSibling); + m_current = firstChild; + return *this; + } + + if (nextSibling) { + m_current = nextSibling; + return *this; + } + + m_current = m_ancestorSiblingStack.takeLast(); + +#if !ASSERT_DISABLED + // Drop the assertion when the iterator reaches the end. + if (!m_current) + m_assertions.dropEventDispatchAssertion(); +#endif + + return *this; +} + +ALWAYS_INLINE ElementDescendantIterator& ElementDescendantIterator::operator--() +{ + ASSERT(m_current); + ASSERT(!m_assertions.domTreeHasMutated()); + + Element* previousSibling = ElementTraversal::previousSibling(*m_current); + + if (!previousSibling) { + m_current = m_current->parentElement(); + // The stack optimizes for forward traversal only, this just maintains consistency. + if (m_current->nextSibling() && m_current->nextSibling() == m_ancestorSiblingStack.last()) + m_ancestorSiblingStack.removeLast(); + return *this; + } + + Element* deepestSibling = previousSibling; + while (Element* lastChild = ElementTraversal::lastChild(*deepestSibling)) + deepestSibling = lastChild; + ASSERT(deepestSibling); + + if (deepestSibling != previousSibling) + m_ancestorSiblingStack.append(m_current); -template <typename ElementType> -inline ElementDescendantConstIterator<ElementType>::ElementDescendantConstIterator(const ContainerNode& root) - : ElementConstIterator<ElementType>(&root) + m_current = deepestSibling; +#if !ASSERT_DISABLED + // Drop the assertion when the iterator reaches the end. + if (!m_current) + m_assertions.dropEventDispatchAssertion(); +#endif + + return *this; +} + +inline Element& ElementDescendantIterator::operator*() { + ASSERT(m_current); + ASSERT(!m_assertions.domTreeHasMutated()); + return *m_current; } -template <typename ElementType> -inline ElementDescendantConstIterator<ElementType>::ElementDescendantConstIterator(const ContainerNode& root, const ElementType* current) - : ElementConstIterator<ElementType>(&root, current) +inline Element* ElementDescendantIterator::operator->() { + ASSERT(m_current); + ASSERT(!m_assertions.domTreeHasMutated()); + return m_current; } -template <typename ElementType> -inline ElementDescendantConstIterator<ElementType>& ElementDescendantConstIterator<ElementType>::operator++() +inline bool ElementDescendantIterator::operator==(const ElementDescendantIterator& other) const { - return static_cast<ElementDescendantConstIterator<ElementType>&>(ElementConstIterator<ElementType>::traverseNext()); + ASSERT(!m_assertions.domTreeHasMutated()); + return m_current == other.m_current; } -// ElementDescendantIteratorAdapter +inline bool ElementDescendantIterator::operator!=(const ElementDescendantIterator& other) const +{ + return !(*this == other); +} -template <typename ElementType> -inline ElementDescendantIteratorAdapter<ElementType>::ElementDescendantIteratorAdapter(ContainerNode& root) - : m_root(root) +// ElementDescendantConstIterator + +inline ElementDescendantConstIterator::ElementDescendantConstIterator() + : m_current(nullptr) { } -template <typename ElementType> -inline ElementDescendantIterator<ElementType> ElementDescendantIteratorAdapter<ElementType>::begin() +inline ElementDescendantConstIterator::ElementDescendantConstIterator(const Element* current) + : m_current(current) +#if !ASSERT_DISABLED + , m_assertions(current) +#endif { - return ElementDescendantIterator<ElementType>(m_root, Traversal<ElementType>::firstWithin(&m_root)); + m_ancestorSiblingStack.uncheckedAppend(nullptr); } -template <typename ElementType> -inline ElementDescendantIterator<ElementType> ElementDescendantIteratorAdapter<ElementType>::end() +inline void ElementDescendantConstIterator::dropAssertions() { - return ElementDescendantIterator<ElementType>(m_root); +#if !ASSERT_DISABLED + m_assertions.clear(); +#endif } - -template <typename ElementType> -inline ElementDescendantIterator<ElementType> ElementDescendantIteratorAdapter<ElementType>::beginAt(ElementType& descendant) + +ALWAYS_INLINE ElementDescendantConstIterator& ElementDescendantConstIterator::operator++() { - ASSERT(descendant.isDescendantOf(&m_root)); - return ElementDescendantIterator<ElementType>(m_root, &descendant); + ASSERT(m_current); + ASSERT(!m_assertions.domTreeHasMutated()); + + Element* firstChild = ElementTraversal::firstChild(*m_current); + Element* nextSibling = ElementTraversal::nextSibling(*m_current); + + if (firstChild) { + if (nextSibling) + m_ancestorSiblingStack.append(nextSibling); + m_current = firstChild; + return *this; + } + + if (nextSibling) { + m_current = nextSibling; + return *this; + } + + m_current = m_ancestorSiblingStack.takeLast(); + +#if !ASSERT_DISABLED + // Drop the assertion when the iterator reaches the end. + if (!m_current) + m_assertions.dropEventDispatchAssertion(); +#endif + + return *this; } -template <typename ElementType> -inline ElementDescendantIterator<ElementType> ElementDescendantIteratorAdapter<ElementType>::from(Element& descendant) +inline const Element& ElementDescendantConstIterator::operator*() const { - ASSERT(descendant.isDescendantOf(&m_root)); - if (isElementOfType<const ElementType>(descendant)) - return ElementDescendantIterator<ElementType>(m_root, static_cast<ElementType*>(&descendant)); - ElementType* next = Traversal<ElementType>::next(&m_root, &descendant); - return ElementDescendantIterator<ElementType>(m_root, next); + ASSERT(m_current); + ASSERT(!m_assertions.domTreeHasMutated()); + return *m_current; } -template <typename ElementType> -inline ElementType* ElementDescendantIteratorAdapter<ElementType>::first() +inline const Element* ElementDescendantConstIterator::operator->() const { - return Traversal<ElementType>::firstWithin(&m_root); + ASSERT(m_current); + ASSERT(!m_assertions.domTreeHasMutated()); + return m_current; } -template <typename ElementType> -inline ElementType* ElementDescendantIteratorAdapter<ElementType>::last() +inline bool ElementDescendantConstIterator::operator==(const ElementDescendantConstIterator& other) const { - return Traversal<ElementType>::lastWithin(&m_root); + ASSERT(!m_assertions.domTreeHasMutated()); + return m_current == other.m_current; } -// ElementDescendantConstIteratorAdapter +inline bool ElementDescendantConstIterator::operator!=(const ElementDescendantConstIterator& other) const +{ + return !(*this == other); +} + +// ElementDescendantIteratorAdapter -template <typename ElementType> -inline ElementDescendantConstIteratorAdapter<ElementType>::ElementDescendantConstIteratorAdapter(const ContainerNode& root) +inline ElementDescendantIteratorAdapter::ElementDescendantIteratorAdapter(ContainerNode& root) : m_root(root) { } -template <typename ElementType> -inline ElementDescendantConstIterator<ElementType> ElementDescendantConstIteratorAdapter<ElementType>::begin() const +inline ElementDescendantIterator ElementDescendantIteratorAdapter::begin() { - return ElementDescendantConstIterator<ElementType>(m_root, Traversal<ElementType>::firstWithin(&m_root)); + return ElementDescendantIterator(ElementTraversal::firstChild(m_root)); } -template <typename ElementType> -inline ElementDescendantConstIterator<ElementType> ElementDescendantConstIteratorAdapter<ElementType>::end() const +inline ElementDescendantIterator ElementDescendantIteratorAdapter::end() { - return ElementDescendantConstIterator<ElementType>(m_root); + return ElementDescendantIterator(); } -template <typename ElementType> -inline ElementDescendantConstIterator<ElementType> ElementDescendantConstIteratorAdapter<ElementType>::beginAt(const ElementType& descendant) const +inline ElementDescendantIterator ElementDescendantIteratorAdapter::last() { - ASSERT(descendant.isDescendantOf(&m_root)); - return ElementDescendantConstIterator<ElementType>(m_root, &descendant); + return ElementDescendantIterator(ElementTraversal::lastWithin(m_root)); } -template <typename ElementType> -inline ElementDescendantConstIterator<ElementType> ElementDescendantConstIteratorAdapter<ElementType>::from(const Element& descendant) const +// ElementDescendantConstIteratorAdapter + +inline ElementDescendantConstIteratorAdapter::ElementDescendantConstIteratorAdapter(const ContainerNode& root) + : m_root(root) { - ASSERT(descendant.isDescendantOf(&m_root)); - if (isElementOfType<const ElementType>(descendant)) - return ElementDescendantConstIterator<ElementType>(m_root, static_cast<const ElementType*>(&descendant)); - const ElementType* next = Traversal<ElementType>::next(&m_root, &descendant); - return ElementDescendantConstIterator<ElementType>(m_root, next); } -template <typename ElementType> -inline const ElementType* ElementDescendantConstIteratorAdapter<ElementType>::first() const +inline ElementDescendantConstIterator ElementDescendantConstIteratorAdapter::begin() const { - return Traversal<ElementType>::firstWithin(&m_root); + return ElementDescendantConstIterator(ElementTraversal::firstChild(m_root)); } -template <typename ElementType> -inline const ElementType* ElementDescendantConstIteratorAdapter<ElementType>::last() const +inline ElementDescendantConstIterator ElementDescendantConstIteratorAdapter::end() const { - return Traversal<ElementType>::lastWithin(&m_root); + return ElementDescendantConstIterator(); +} + +inline ElementDescendantConstIterator ElementDescendantConstIteratorAdapter::last() const +{ + return ElementDescendantConstIterator(ElementTraversal::lastWithin(m_root)); } // Standalone functions -template <typename ElementType> -inline ElementDescendantIteratorAdapter<ElementType> descendantsOfType(ContainerNode& root) +inline ElementDescendantIteratorAdapter elementDescendants(ContainerNode& root) { - return ElementDescendantIteratorAdapter<ElementType>(root); + return ElementDescendantIteratorAdapter(root); } -template <typename ElementType> -inline ElementDescendantConstIteratorAdapter<ElementType> descendantsOfType(const ContainerNode& root) +inline ElementDescendantConstIteratorAdapter elementDescendants(const ContainerNode& root) { - return ElementDescendantConstIteratorAdapter<ElementType>(root); + return ElementDescendantConstIteratorAdapter(root); } -} +} // namespace WebCore -#endif +namespace std { +template <> struct iterator_traits<WebCore::ElementDescendantIterator> { + typedef WebCore::Element value_type; +}; +template <> struct iterator_traits<WebCore::ElementDescendantConstIterator> { + typedef const WebCore::Element value_type; +}; +} diff --git a/Source/WebCore/dom/ElementIterator.h b/Source/WebCore/dom/ElementIterator.h index a155ed679..da7d935f8 100644 --- a/Source/WebCore/dom/ElementIterator.h +++ b/Source/WebCore/dom/ElementIterator.h @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ElementIterator_h -#define ElementIterator_h +#pragma once #include "ElementTraversal.h" @@ -40,8 +39,8 @@ public: ElementIterator(const ContainerNode* root); ElementIterator(const ContainerNode* root, ElementType* current); - ElementType& operator*(); - ElementType* operator->(); + ElementType& operator*() const; + ElementType* operator->() const; bool operator==(const ElementIterator& other) const; bool operator!=(const ElementIterator& other) const; @@ -53,6 +52,8 @@ public: ElementIterator& traverseNextSkippingChildren(); ElementIterator& traverseAncestor(); + void dropAssertions(); + private: const ContainerNode* m_root; ElementType* m_current; @@ -81,6 +82,8 @@ public: ElementConstIterator& traverseNextSkippingChildren(); ElementConstIterator& traverseAncestor(); + void dropAssertions(); + private: const ContainerNode* m_root; const ElementType* m_current; @@ -114,7 +117,7 @@ inline ElementIterator<ElementType>& ElementIterator<ElementType>::traverseNext( { ASSERT(m_current); ASSERT(!m_assertions.domTreeHasMutated()); - m_current = Traversal<ElementType>::next(m_current, m_root); + m_current = Traversal<ElementType>::next(*m_current, m_root); #if !ASSERT_DISABLED // Drop the assertion when the iterator reaches the end. if (!m_current) @@ -128,7 +131,7 @@ inline ElementIterator<ElementType>& ElementIterator<ElementType>::traversePrevi { ASSERT(m_current); ASSERT(!m_assertions.domTreeHasMutated()); - m_current = Traversal<ElementType>::previous(m_current, m_root); + m_current = Traversal<ElementType>::previous(*m_current, m_root); #if !ASSERT_DISABLED // Drop the assertion when the iterator reaches the end. if (!m_current) @@ -142,7 +145,7 @@ inline ElementIterator<ElementType>& ElementIterator<ElementType>::traverseNextS { ASSERT(m_current); ASSERT(!m_assertions.domTreeHasMutated()); - m_current = Traversal<ElementType>::nextSibling(m_current); + m_current = Traversal<ElementType>::nextSibling(*m_current); #if !ASSERT_DISABLED // Drop the assertion when the iterator reaches the end. if (!m_current) @@ -156,7 +159,7 @@ inline ElementIterator<ElementType>& ElementIterator<ElementType>::traversePrevi { ASSERT(m_current); ASSERT(!m_assertions.domTreeHasMutated()); - m_current = Traversal<ElementType>::previousSibling(m_current); + m_current = Traversal<ElementType>::previousSibling(*m_current); #if !ASSERT_DISABLED // Drop the assertion when the iterator reaches the end. if (!m_current) @@ -170,7 +173,7 @@ inline ElementIterator<ElementType>& ElementIterator<ElementType>::traverseNextS { ASSERT(m_current); ASSERT(!m_assertions.domTreeHasMutated()); - m_current = Traversal<ElementType>::nextSkippingChildren(m_current, m_root); + m_current = Traversal<ElementType>::nextSkippingChildren(*m_current, m_root); #if !ASSERT_DISABLED // Drop the assertion when the iterator reaches the end. if (!m_current) @@ -180,15 +183,29 @@ inline ElementIterator<ElementType>& ElementIterator<ElementType>::traverseNextS } template <typename ElementType> -inline ElementType* findElementAncestorOfType(const Element& current) +inline void ElementIterator<ElementType>::dropAssertions() +{ +#if !ASSERT_DISABLED + m_assertions.clear(); +#endif +} + +template <typename ElementType> +inline ElementType* findElementAncestorOfType(const Node& current) { for (Element* ancestor = current.parentElement(); ancestor; ancestor = ancestor->parentElement()) { - if (isElementOfType<const ElementType>(*ancestor)) - return static_cast<ElementType*>(ancestor); + if (is<ElementType>(*ancestor)) + return downcast<ElementType>(ancestor); } return nullptr; } +template <> +inline Element* findElementAncestorOfType<Element>(const Node& current) +{ + return current.parentElement(); +} + template <typename ElementType> inline ElementIterator<ElementType>& ElementIterator<ElementType>::traverseAncestor() { @@ -205,7 +222,7 @@ inline ElementIterator<ElementType>& ElementIterator<ElementType>::traverseAnces } template <typename ElementType> -inline ElementType& ElementIterator<ElementType>::operator*() +inline ElementType& ElementIterator<ElementType>::operator*() const { ASSERT(m_current); ASSERT(!m_assertions.domTreeHasMutated()); @@ -213,7 +230,7 @@ inline ElementType& ElementIterator<ElementType>::operator*() } template <typename ElementType> -inline ElementType* ElementIterator<ElementType>::operator->() +inline ElementType* ElementIterator<ElementType>::operator->() const { ASSERT(m_current); ASSERT(!m_assertions.domTreeHasMutated()); @@ -258,7 +275,7 @@ inline ElementConstIterator<ElementType>& ElementConstIterator<ElementType>::tra { ASSERT(m_current); ASSERT(!m_assertions.domTreeHasMutated()); - m_current = Traversal<ElementType>::next(m_current, m_root); + m_current = Traversal<ElementType>::next(*m_current, m_root); #if !ASSERT_DISABLED // Drop the assertion when the iterator reaches the end. if (!m_current) @@ -272,7 +289,7 @@ inline ElementConstIterator<ElementType>& ElementConstIterator<ElementType>::tra { ASSERT(m_current); ASSERT(!m_assertions.domTreeHasMutated()); - m_current = Traversal<ElementType>::previous(m_current, m_root); + m_current = Traversal<ElementType>::previous(*m_current, m_root); #if !ASSERT_DISABLED // Drop the assertion when the iterator reaches the end. if (!m_current) @@ -286,7 +303,7 @@ inline ElementConstIterator<ElementType>& ElementConstIterator<ElementType>::tra { ASSERT(m_current); ASSERT(!m_assertions.domTreeHasMutated()); - m_current = Traversal<ElementType>::nextSibling(m_current); + m_current = Traversal<ElementType>::nextSibling(*m_current); #if !ASSERT_DISABLED // Drop the assertion when the iterator reaches the end. if (!m_current) @@ -300,7 +317,7 @@ inline ElementConstIterator<ElementType>& ElementConstIterator<ElementType>::tra { ASSERT(m_current); ASSERT(!m_assertions.domTreeHasMutated()); - m_current = Traversal<ElementType>::previousSibling(m_current); + m_current = Traversal<ElementType>::previousSibling(*m_current); #if !ASSERT_DISABLED // Drop the assertion when the iterator reaches the end. if (!m_current) @@ -314,7 +331,7 @@ inline ElementConstIterator<ElementType>& ElementConstIterator<ElementType>::tra { ASSERT(m_current); ASSERT(!m_assertions.domTreeHasMutated()); - m_current = Traversal<ElementType>::nextSkippingChildren(m_current, m_root); + m_current = Traversal<ElementType>::nextSkippingChildren(*m_current, m_root); #if !ASSERT_DISABLED // Drop the assertion when the iterator reaches the end. if (!m_current) @@ -339,6 +356,14 @@ inline ElementConstIterator<ElementType>& ElementConstIterator<ElementType>::tra } template <typename ElementType> +inline void ElementConstIterator<ElementType>::dropAssertions() +{ +#if !ASSERT_DISABLED + m_assertions.clear(); +#endif +} + +template <typename ElementType> inline const ElementType& ElementConstIterator<ElementType>::operator*() const { ASSERT(m_current); @@ -368,10 +393,8 @@ inline bool ElementConstIterator<ElementType>::operator!=(const ElementConstIter return !(*this == other); } -} +} // namespace WebCore #include "ElementAncestorIterator.h" #include "ElementChildIterator.h" -#include "ElementDescendantIterator.h" - -#endif +#include "TypedElementDescendantIterator.h" diff --git a/Source/WebCore/dom/ElementIteratorAssertions.h b/Source/WebCore/dom/ElementIteratorAssertions.h index c054ba371..df3703aca 100644 --- a/Source/WebCore/dom/ElementIteratorAssertions.h +++ b/Source/WebCore/dom/ElementIteratorAssertions.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Apple Inc. All rights reserved. + * Copyright (C) 2013-2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,50 +23,51 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ElementIteratorAssertions_h -#define ElementIteratorAssertions_h +#pragma once -#include "Document.h" #include "Element.h" +#include "NoEventDispatchAssertion.h" namespace WebCore { class ElementIteratorAssertions { public: - ElementIteratorAssertions(); - ElementIteratorAssertions(const Element* first); + ElementIteratorAssertions(const Node* first = nullptr); bool domTreeHasMutated() const; void dropEventDispatchAssertion(); + void clear(); private: const Document* m_document; uint64_t m_initialDOMTreeVersion; - OwnPtr<NoEventDispatchAssertion> m_noEventDispatchAssertion; + std::optional<NoEventDispatchAssertion> m_eventDispatchAssertion; }; -inline ElementIteratorAssertions::ElementIteratorAssertions() - : m_document(nullptr) - , m_initialDOMTreeVersion(0) -{ -} +// FIXME: No real point in doing these as inlines; they are for debugging and we usually turn off inlining in debug builds. -inline ElementIteratorAssertions::ElementIteratorAssertions(const Element* first) +inline ElementIteratorAssertions::ElementIteratorAssertions(const Node* first) : m_document(first ? &first->document() : nullptr) - , m_initialDOMTreeVersion(m_document ? m_document->domTreeVersion() : 0) - , m_noEventDispatchAssertion(m_document ? adoptPtr(new NoEventDispatchAssertion) : nullptr) + , m_initialDOMTreeVersion(first ? m_document->domTreeVersion() : 0) { + if (first) + m_eventDispatchAssertion = NoEventDispatchAssertion(); } inline bool ElementIteratorAssertions::domTreeHasMutated() const { - return m_initialDOMTreeVersion && m_document && m_document->domTreeVersion() != m_initialDOMTreeVersion; + return m_document && m_document->domTreeVersion() != m_initialDOMTreeVersion; } inline void ElementIteratorAssertions::dropEventDispatchAssertion() { - m_noEventDispatchAssertion = nullptr; + m_eventDispatchAssertion = std::nullopt; } +inline void ElementIteratorAssertions::clear() +{ + m_document = nullptr; + m_initialDOMTreeVersion = 0; + m_eventDispatchAssertion = std::nullopt; } -#endif +} // namespace WebCore diff --git a/Source/WebCore/dom/ElementRareData.cpp b/Source/WebCore/dom/ElementRareData.cpp index d35215e20..aca3463a7 100644 --- a/Source/WebCore/dom/ElementRareData.cpp +++ b/Source/WebCore/dom/ElementRareData.cpp @@ -34,14 +34,19 @@ namespace WebCore { struct SameSizeAsElementRareData : NodeRareData { - short indices[2]; - unsigned bitfields; + int tabIndex; + unsigned short childIndex; +#if ENABLE(FULLSCREEN_API) + unsigned bitfields : 11; +#else + unsigned bitfields : 10; +#endif RegionOversetState regionOversetState; LayoutSize sizeForResizing; - IntSize scrollOffset; - void* pointers[7]; + IntPoint savedLayerScrollPosition; + void* pointers[8]; }; -COMPILE_ASSERT(sizeof(ElementRareData) == sizeof(SameSizeAsElementRareData), ElementRareDataShouldStaySmall); +static_assert(sizeof(ElementRareData) == sizeof(SameSizeAsElementRareData), "ElementRareData should stay small"); } // namespace WebCore diff --git a/Source/WebCore/dom/ElementRareData.h b/Source/WebCore/dom/ElementRareData.h index 5a00ea298..4b32068b4 100644 --- a/Source/WebCore/dom/ElementRareData.h +++ b/Source/WebCore/dom/ElementRareData.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009, 2010, 2014, 2016 Apple Inc. All rights reserved. * Copyright (C) 2008 David Smith <catfish.man@gmail.com> * * This library is free software; you can redistribute it and/or @@ -19,147 +19,146 @@ * */ -#ifndef ElementRareData_h -#define ElementRareData_h +#pragma once -#include "ClassList.h" +#include "CustomElementReactionQueue.h" +#include "DOMTokenList.h" #include "DatasetDOMStringMap.h" +#include "JSCustomElementInterface.h" #include "NamedNodeMap.h" #include "NodeRareData.h" #include "PseudoElement.h" #include "RenderElement.h" #include "ShadowRoot.h" #include "StyleInheritedData.h" -#include <wtf/OwnPtr.h> namespace WebCore { class ElementRareData : public NodeRareData { public: - static PassOwnPtr<ElementRareData> create(RenderElement* renderer) { return adoptPtr(new ElementRareData(renderer)); } - + explicit ElementRareData(RenderElement*); ~ElementRareData(); - void setBeforePseudoElement(PassRefPtr<PseudoElement>); - void setAfterPseudoElement(PassRefPtr<PseudoElement>); + void setBeforePseudoElement(RefPtr<PseudoElement>&&); + void setAfterPseudoElement(RefPtr<PseudoElement>&&); PseudoElement* beforePseudoElement() const { return m_beforePseudoElement.get(); } PseudoElement* afterPseudoElement() const { return m_afterPseudoElement.get(); } void resetComputedStyle(); - void resetDynamicRestyleObservations(); + void resetStyleRelations(); - short tabIndex() const { return m_tabIndex; } - void setTabIndexExplicitly(short index) { m_tabIndex = index; m_tabIndexWasSetExplicitly = true; } + int tabIndex() const { return m_tabIndex; } + void setTabIndexExplicitly(int index) { m_tabIndex = index; m_tabIndexWasSetExplicitly = true; } bool tabIndexSetExplicitly() const { return m_tabIndexWasSetExplicitly; } void clearTabIndexExplicitly() { m_tabIndex = 0; m_tabIndexWasSetExplicitly = false; } bool needsFocusAppearanceUpdateSoonAfterAttach() const { return m_needsFocusAppearanceUpdateSoonAfterAttach; } void setNeedsFocusAppearanceUpdateSoonAfterAttach(bool needs) { m_needsFocusAppearanceUpdateSoonAfterAttach = needs; } + bool styleAffectedByActive() const { return m_styleAffectedByActive; } + void setStyleAffectedByActive(bool value) { m_styleAffectedByActive = value; } + bool styleAffectedByEmpty() const { return m_styleAffectedByEmpty; } void setStyleAffectedByEmpty(bool value) { m_styleAffectedByEmpty = value; } - bool isInCanvasSubtree() const { return m_isInCanvasSubtree; } - void setIsInCanvasSubtree(bool value) { m_isInCanvasSubtree = value; } - - bool isInsideRegion() const { return m_isInsideRegion; } - void setIsInsideRegion(bool value) { m_isInsideRegion = value; } + bool styleAffectedByFocusWithin() const { return m_styleAffectedByFocusWithin; } + void setStyleAffectedByFocusWithin(bool value) { m_styleAffectedByFocusWithin = value; } RegionOversetState regionOversetState() const { return m_regionOversetState; } void setRegionOversetState(RegionOversetState state) { m_regionOversetState = state; } + bool isNamedFlowContentElement() const { return m_isNamedFlowContentElement; } + void setIsNamedFlowContentElement(bool value) { m_isNamedFlowContentElement = value; } + #if ENABLE(FULLSCREEN_API) bool containsFullScreenElement() { return m_containsFullScreenElement; } void setContainsFullScreenElement(bool value) { m_containsFullScreenElement = value; } #endif - bool childrenAffectedByActive() const { return m_childrenAffectedByActive; } - void setChildrenAffectedByActive(bool value) { m_childrenAffectedByActive = value; } bool childrenAffectedByDrag() const { return m_childrenAffectedByDrag; } void setChildrenAffectedByDrag(bool value) { m_childrenAffectedByDrag = value; } bool childrenAffectedByLastChildRules() const { return m_childrenAffectedByLastChildRules; } void setChildrenAffectedByLastChildRules(bool value) { m_childrenAffectedByLastChildRules = value; } - bool childrenAffectedByForwardPositionalRules() const { return m_childrenAffectedByForwardPositionalRules; } - void setChildrenAffectedByForwardPositionalRules(bool value) { m_childrenAffectedByForwardPositionalRules = value; } bool childrenAffectedByBackwardPositionalRules() const { return m_childrenAffectedByBackwardPositionalRules; } void setChildrenAffectedByBackwardPositionalRules(bool value) { m_childrenAffectedByBackwardPositionalRules = value; } + bool childrenAffectedByPropertyBasedBackwardPositionalRules() const { return m_childrenAffectedByPropertyBasedBackwardPositionalRules; } + void setChildrenAffectedByPropertyBasedBackwardPositionalRules(bool value) { m_childrenAffectedByPropertyBasedBackwardPositionalRules = value; } + unsigned childIndex() const { return m_childIndex; } void setChildIndex(unsigned index) { m_childIndex = index; } + static ptrdiff_t childIndexMemoryOffset() { return OBJECT_OFFSETOF(ElementRareData, m_childIndex); } void clearShadowRoot() { m_shadowRoot = nullptr; } ShadowRoot* shadowRoot() const { return m_shadowRoot.get(); } - void setShadowRoot(PassRefPtr<ShadowRoot> shadowRoot) { m_shadowRoot = shadowRoot; } + void setShadowRoot(RefPtr<ShadowRoot>&& shadowRoot) { m_shadowRoot = WTFMove(shadowRoot); } + + CustomElementReactionQueue* customElementReactionQueue() { return m_customElementReactionQueue.get(); } + void setCustomElementReactionQueue(std::unique_ptr<CustomElementReactionQueue>&& queue) { m_customElementReactionQueue = WTFMove(queue); } NamedNodeMap* attributeMap() const { return m_attributeMap.get(); } - void setAttributeMap(PassOwnPtr<NamedNodeMap> attributeMap) { m_attributeMap = attributeMap; } + void setAttributeMap(std::unique_ptr<NamedNodeMap> attributeMap) { m_attributeMap = WTFMove(attributeMap); } RenderStyle* computedStyle() const { return m_computedStyle.get(); } - void setComputedStyle(PassRef<RenderStyle> computedStyle) { m_computedStyle = std::move(computedStyle); } + void setComputedStyle(std::unique_ptr<RenderStyle> computedStyle) { m_computedStyle = WTFMove(computedStyle); } - ClassList* classList() const { return m_classList.get(); } - void setClassList(std::unique_ptr<ClassList> classList) { m_classList = std::move(classList); } - void clearClassListValueForQuirksMode() - { - if (!m_classList) - return; - m_classList->clearValueForQuirksMode(); - } + DOMTokenList* classList() const { return m_classList.get(); } + void setClassList(std::unique_ptr<DOMTokenList> classList) { m_classList = WTFMove(classList); } DatasetDOMStringMap* dataset() const { return m_dataset.get(); } - void setDataset(std::unique_ptr<DatasetDOMStringMap> dataset) { m_dataset = std::move(dataset); } + void setDataset(std::unique_ptr<DatasetDOMStringMap> dataset) { m_dataset = WTFMove(dataset); } LayoutSize minimumSizeForResizing() const { return m_minimumSizeForResizing; } void setMinimumSizeForResizing(LayoutSize size) { m_minimumSizeForResizing = size; } - IntSize savedLayerScrollOffset() const { return m_savedLayerScrollOffset; } - void setSavedLayerScrollOffset(IntSize size) { m_savedLayerScrollOffset = size; } + IntPoint savedLayerScrollPosition() const { return m_savedLayerScrollPosition; } + void setSavedLayerScrollPosition(IntPoint position) { m_savedLayerScrollPosition = position; } -#if ENABLE(SVG) bool hasPendingResources() const { return m_hasPendingResources; } void setHasPendingResources(bool has) { m_hasPendingResources = has; } -#endif + + bool hasDisplayContents() const { return m_hasDisplayContents; } + void setHasDisplayContents(bool value) { m_hasDisplayContents = value; } private: - short m_tabIndex; + int m_tabIndex; unsigned short m_childIndex; unsigned m_tabIndexWasSetExplicitly : 1; unsigned m_needsFocusAppearanceUpdateSoonAfterAttach : 1; + unsigned m_styleAffectedByActive : 1; unsigned m_styleAffectedByEmpty : 1; - unsigned m_isInCanvasSubtree : 1; + unsigned m_styleAffectedByFocusWithin : 1; #if ENABLE(FULLSCREEN_API) unsigned m_containsFullScreenElement : 1; #endif -#if ENABLE(SVG) unsigned m_hasPendingResources : 1; -#endif unsigned m_childrenAffectedByHover : 1; - unsigned m_childrenAffectedByActive : 1; unsigned m_childrenAffectedByDrag : 1; // Bits for dynamic child matching. // We optimize for :first-child and :last-child. The other positional child selectors like nth-child or // *-child-of-type, we will just give up and re-evaluate whenever children change at all. unsigned m_childrenAffectedByLastChildRules : 1; - unsigned m_childrenAffectedByForwardPositionalRules : 1; unsigned m_childrenAffectedByBackwardPositionalRules : 1; + unsigned m_childrenAffectedByPropertyBasedBackwardPositionalRules : 1; + unsigned m_hasDisplayContents : 1; + unsigned m_isNamedFlowContentElement : 1; - unsigned m_isInsideRegion : 1; RegionOversetState m_regionOversetState; LayoutSize m_minimumSizeForResizing; - IntSize m_savedLayerScrollOffset; - RefPtr<RenderStyle> m_computedStyle; + IntPoint m_savedLayerScrollPosition; + std::unique_ptr<RenderStyle> m_computedStyle; std::unique_ptr<DatasetDOMStringMap> m_dataset; - std::unique_ptr<ClassList> m_classList; + std::unique_ptr<DOMTokenList> m_classList; RefPtr<ShadowRoot> m_shadowRoot; - OwnPtr<NamedNodeMap> m_attributeMap; + std::unique_ptr<CustomElementReactionQueue> m_customElementReactionQueue; + std::unique_ptr<NamedNodeMap> m_attributeMap; RefPtr<PseudoElement> m_beforePseudoElement; RefPtr<PseudoElement> m_afterPseudoElement; - explicit ElementRareData(RenderElement*); void releasePseudoElement(PseudoElement*); }; @@ -174,21 +173,20 @@ inline ElementRareData::ElementRareData(RenderElement* renderer) , m_childIndex(0) , m_tabIndexWasSetExplicitly(false) , m_needsFocusAppearanceUpdateSoonAfterAttach(false) + , m_styleAffectedByActive(false) , m_styleAffectedByEmpty(false) - , m_isInCanvasSubtree(false) + , m_styleAffectedByFocusWithin(false) #if ENABLE(FULLSCREEN_API) , m_containsFullScreenElement(false) #endif -#if ENABLE(SVG) , m_hasPendingResources(false) -#endif , m_childrenAffectedByHover(false) - , m_childrenAffectedByActive(false) , m_childrenAffectedByDrag(false) , m_childrenAffectedByLastChildRules(false) - , m_childrenAffectedByForwardPositionalRules(false) , m_childrenAffectedByBackwardPositionalRules(false) - , m_isInsideRegion(false) + , m_childrenAffectedByPropertyBasedBackwardPositionalRules(false) + , m_hasDisplayContents(false) + , m_isNamedFlowContentElement(false) , m_regionOversetState(RegionUndefined) , m_minimumSizeForResizing(defaultMinimumSizeForResizing()) { @@ -201,34 +199,34 @@ inline ElementRareData::~ElementRareData() ASSERT(!m_afterPseudoElement); } -inline void ElementRareData::setBeforePseudoElement(PassRefPtr<PseudoElement> pseudoElement) +inline void ElementRareData::setBeforePseudoElement(RefPtr<PseudoElement>&& pseudoElement) { ASSERT(!m_beforePseudoElement || !pseudoElement); - m_beforePseudoElement = pseudoElement; + m_beforePseudoElement = WTFMove(pseudoElement); } -inline void ElementRareData::setAfterPseudoElement(PassRefPtr<PseudoElement> pseudoElement) +inline void ElementRareData::setAfterPseudoElement(RefPtr<PseudoElement>&& pseudoElement) { ASSERT(!m_afterPseudoElement || !pseudoElement); - m_afterPseudoElement = pseudoElement; + m_afterPseudoElement = WTFMove(pseudoElement); } inline void ElementRareData::resetComputedStyle() { m_computedStyle = nullptr; - setStyleAffectedByEmpty(false); - setChildIndex(0); + m_hasDisplayContents = false; } -inline void ElementRareData::resetDynamicRestyleObservations() +inline void ElementRareData::resetStyleRelations() { - setChildrenAffectedByActive(false); + setStyleAffectedByEmpty(false); + setStyleAffectedByFocusWithin(false); + setChildIndex(0); + setStyleAffectedByActive(false); setChildrenAffectedByDrag(false); setChildrenAffectedByLastChildRules(false); - setChildrenAffectedByForwardPositionalRules(false); setChildrenAffectedByBackwardPositionalRules(false); + setChildrenAffectedByPropertyBasedBackwardPositionalRules(false); } -} // namespace - -#endif // ElementRareData_h +} // namespace WebCore diff --git a/Source/WebCore/dom/ElementTraversal.h b/Source/WebCore/dom/ElementTraversal.h index 03a7c98f1..57f42e234 100644 --- a/Source/WebCore/dom/ElementTraversal.h +++ b/Source/WebCore/dom/ElementTraversal.h @@ -22,8 +22,7 @@ * */ -#ifndef ElementTraversal_h -#define ElementTraversal_h +#pragma once #include "Element.h" #include "NodeTraversal.h" @@ -34,335 +33,257 @@ template <typename ElementType> class Traversal { public: // First or last ElementType child of the node. - static ElementType* firstChild(const Node*); - static ElementType* firstChild(const ContainerNode*); - static ElementType* lastChild(const Node*); - static ElementType* lastChild(const ContainerNode*); + static ElementType* firstChild(const Node&); + static ElementType* firstChild(const ContainerNode&); + static ElementType* lastChild(const Node&); + static ElementType* lastChild(const ContainerNode&); // First or last ElementType descendant of the node. For Elements firstWithin is always the same as first child. - static ElementType* firstWithin(const Node*); - static ElementType* firstWithin(const ContainerNode*); - static ElementType* lastWithin(const Node*); - static ElementType* lastWithin(const ContainerNode*); + static ElementType* firstWithin(const Node&); + static ElementType* firstWithin(const ContainerNode&); + static ElementType* lastWithin(const Node&); + static ElementType* lastWithin(const ContainerNode&); // Pre-order traversal skipping non-ElementType nodes. - static ElementType* next(const Node*); - static ElementType* next(const Node*, const Node* stayWithin); - static ElementType* next(const ContainerNode*); - static ElementType* next(const ContainerNode*, const Node* stayWithin); - static ElementType* previous(const Node*); - static ElementType* previous(const Node*, const Node* stayWithin); - static ElementType* previous(const ContainerNode*); - static ElementType* previous(const ContainerNode*, const Node* stayWithin); + static ElementType* next(const Node&); + static ElementType* next(const Node&, const Node* stayWithin); + static ElementType* next(const ContainerNode&); + static ElementType* next(const ContainerNode&, const Node* stayWithin); + static ElementType* previous(const Node&); + static ElementType* previous(const Node&, const Node* stayWithin); // Next or previous ElementType sibling if there is one. - static ElementType* nextSibling(const Node*); - static ElementType* nextSibling(const ContainerNode*); - static ElementType* previousSibling(const Node*); - static ElementType* previousSibling(const ContainerNode*); + static ElementType* nextSibling(const Node&); + static ElementType* previousSibling(const Node&); // Like next, but skips children. - static ElementType* nextSkippingChildren(const Node*); - static ElementType* nextSkippingChildren(const Node*, const Node* stayWithin); - static ElementType* nextSkippingChildren(const ContainerNode*); - static ElementType* nextSkippingChildren(const ContainerNode*, const Node* stayWithin); + static ElementType* nextSkippingChildren(const Node&); + static ElementType* nextSkippingChildren(const Node&, const Node* stayWithin); private: - template <typename CurrentType> static ElementType* firstChildTemplate(CurrentType*); - template <typename CurrentType> static ElementType* lastChildTemplate(CurrentType*); - template <typename CurrentType> static ElementType* firstWithinTemplate(CurrentType*); - template <typename CurrentType> static ElementType* lastWithinTemplate(CurrentType*); - template <typename CurrentType> static ElementType* nextTemplate(CurrentType*); - template <typename CurrentType> static ElementType* nextTemplate(CurrentType*, const Node* stayWithin); - template <typename CurrentType> static ElementType* previousTemplate(CurrentType*); - template <typename CurrentType> static ElementType* previousTemplate(CurrentType*, const Node* stayWithin); - template <typename CurrentType> static ElementType* nextSiblingTemplate(CurrentType*); - template <typename CurrentType> static ElementType* previousSiblingTemplate(CurrentType*); - template <typename CurrentType> static ElementType* nextSkippingChildrenTemplate(CurrentType*); - template <typename CurrentType> static ElementType* nextSkippingChildrenTemplate(CurrentType*, const Node* stayWithin); + template <typename CurrentType> static ElementType* firstChildTemplate(CurrentType&); + template <typename CurrentType> static ElementType* lastChildTemplate(CurrentType&); + template <typename CurrentType> static ElementType* firstWithinTemplate(CurrentType&); + template <typename CurrentType> static ElementType* lastWithinTemplate(CurrentType&); + template <typename CurrentType> static ElementType* nextTemplate(CurrentType&); + template <typename CurrentType> static ElementType* nextTemplate(CurrentType&, const Node* stayWithin); }; class ElementTraversal : public Traversal<Element> { public: // FIXME: These should go somewhere else. // Pre-order traversal including the pseudo-elements. - static Element* previousIncludingPseudo(const Node*, const Node* = 0); - static Element* nextIncludingPseudo(const Node*, const Node* = 0); - static Element* nextIncludingPseudoSkippingChildren(const Node*, const Node* = 0); + static Element* previousIncludingPseudo(const Node&, const Node* = nullptr); + static Element* nextIncludingPseudo(const Node&, const Node* = nullptr); + static Element* nextIncludingPseudoSkippingChildren(const Node&, const Node* = nullptr); // Utility function to traverse only the element and pseudo-element siblings of a node. - static Element* pseudoAwarePreviousSibling(const Node*); + static Element* pseudoAwarePreviousSibling(const Node&); }; // Specialized for pure Element to exploit the fact that Elements parent is always either another Element or the root. template <> template <typename CurrentType> -inline Element* Traversal<Element>::firstWithinTemplate(CurrentType* current) +inline Element* Traversal<Element>::firstWithinTemplate(CurrentType& current) { return firstChildTemplate(current); } template <> template <typename CurrentType> -inline Element* Traversal<Element>::lastWithinTemplate(CurrentType* current) -{ - Node* node = NodeTraversal::last(current); - while (node && !node->isElementNode()) - node = NodeTraversal::previous(node, current); - return toElement(node); -} - -template <> -template <typename CurrentType> -inline Element* Traversal<Element>::nextTemplate(CurrentType* current) +inline Element* Traversal<Element>::nextTemplate(CurrentType& current) { Node* node = NodeTraversal::next(current); - while (node && !node->isElementNode()) - node = NodeTraversal::nextSkippingChildren(node); - return toElement(node); + while (node && !is<Element>(*node)) + node = NodeTraversal::nextSkippingChildren(*node); + return downcast<Element>(node); } template <> template <typename CurrentType> -inline Element* Traversal<Element>::nextTemplate(CurrentType* current, const Node* stayWithin) +inline Element* Traversal<Element>::nextTemplate(CurrentType& current, const Node* stayWithin) { Node* node = NodeTraversal::next(current, stayWithin); - while (node && !node->isElementNode()) - node = NodeTraversal::nextSkippingChildren(node, stayWithin); - return toElement(node); -} - -template <> -template <typename CurrentType> -inline Element* Traversal<Element>::previousTemplate(CurrentType* current) -{ - Node* node = NodeTraversal::previous(current); - while (node && !node->isElementNode()) - node = NodeTraversal::previous(node); - return toElement(node); -} - -template <> -template <typename CurrentType> -inline Element* Traversal<Element>::previousTemplate(CurrentType* current, const Node* stayWithin) -{ - Node* node = NodeTraversal::previous(current, stayWithin); - while (node && !node->isElementNode()) - node = NodeTraversal::previous(node, stayWithin); - return toElement(node); + while (node && !is<Element>(*node)) + node = NodeTraversal::nextSkippingChildren(*node, stayWithin); + return downcast<Element>(node); } // Generic versions. template <typename ElementType> template <typename CurrentType> -inline ElementType* Traversal<ElementType>::firstChildTemplate(CurrentType* current) +inline ElementType* Traversal<ElementType>::firstChildTemplate(CurrentType& current) { - Node* node = current->firstChild(); - while (node && !isElementOfType<const ElementType>(*node)) + Node* node = current.firstChild(); + while (node && !is<ElementType>(*node)) node = node->nextSibling(); - return static_cast<ElementType*>(node); + return downcast<ElementType>(node); } template <typename ElementType> template <typename CurrentType> -inline ElementType* Traversal<ElementType>::lastChildTemplate(CurrentType* current) +inline ElementType* Traversal<ElementType>::lastChildTemplate(CurrentType& current) { - Node* node = current->lastChild(); - while (node && !isElementOfType<const ElementType>(*node)) + Node* node = current.lastChild(); + while (node && !is<ElementType>(*node)) node = node->previousSibling(); - return static_cast<ElementType*>(node); + return downcast<ElementType>(node); } template <typename ElementType> template <typename CurrentType> -inline ElementType* Traversal<ElementType>::firstWithinTemplate(CurrentType* current) +inline ElementType* Traversal<ElementType>::firstWithinTemplate(CurrentType& current) { - Element* element = Traversal<Element>::firstWithin(current); - while (element && !isElementOfType<const ElementType>(*element)) - element = Traversal<Element>::next(element, current); - return static_cast<ElementType*>(element); + Node* node = current.firstChild(); + while (node && !is<ElementType>(*node)) + node = NodeTraversal::next(*node, ¤t); + return downcast<ElementType>(node); } template <typename ElementType> template <typename CurrentType> -inline ElementType* Traversal<ElementType>::lastWithinTemplate(CurrentType* current) +inline ElementType* Traversal<ElementType>::lastWithinTemplate(CurrentType& current) { - Element* element = Traversal<Element>::lastWithin(current); - while (element && !isElementOfType<const ElementType>(*element)) - element = Traversal<Element>::previous(element, current); - return static_cast<ElementType*>(element); + Node* node = NodeTraversal::last(current); + while (node && !is<ElementType>(*node)) + node = NodeTraversal::previous(*node, ¤t); + return downcast<ElementType>(node); } template <typename ElementType> template <typename CurrentType> -inline ElementType* Traversal<ElementType>::nextTemplate(CurrentType* current) +inline ElementType* Traversal<ElementType>::nextTemplate(CurrentType& current) { - Element* element = Traversal<Element>::next(current); - while (element && !isElementOfType<const ElementType>(*element)) - element = Traversal<Element>::next(element); - return static_cast<ElementType*>(element); + Node* node = NodeTraversal::next(current); + while (node && !is<ElementType>(*node)) + node = NodeTraversal::next(*node); + return downcast<ElementType>(node); } template <typename ElementType> template <typename CurrentType> -inline ElementType* Traversal<ElementType>::nextTemplate(CurrentType* current, const Node* stayWithin) +inline ElementType* Traversal<ElementType>::nextTemplate(CurrentType& current, const Node* stayWithin) { - Element* element = Traversal<Element>::next(current, stayWithin); - while (element && !isElementOfType<const ElementType>(*element)) - element = Traversal<Element>::next(element, stayWithin); - return static_cast<ElementType*>(element); + Node* node = NodeTraversal::next(current, stayWithin); + while (node && !is<ElementType>(*node)) + node = NodeTraversal::next(*node, stayWithin); + return downcast<ElementType>(node); } template <typename ElementType> -template <typename CurrentType> -inline ElementType* Traversal<ElementType>::previousTemplate(CurrentType* current) +inline ElementType* Traversal<ElementType>::previous(const Node& current) { - Element* element = Traversal<Element>::previous(current); - while (element && !isElementOfType<const ElementType>(*element)) - element = Traversal<Element>::previous(element); - return static_cast<ElementType*>(element); + Node* node = NodeTraversal::previous(current); + while (node && !is<ElementType>(*node)) + node = NodeTraversal::previous(*node); + return downcast<ElementType>(node); } template <typename ElementType> -template <typename CurrentType> -inline ElementType* Traversal<ElementType>::previousTemplate(CurrentType* current, const Node* stayWithin) +inline ElementType* Traversal<ElementType>::previous(const Node& current, const Node* stayWithin) { - Element* element = Traversal<Element>::previous(current, stayWithin); - while (element && !isElementOfType<const ElementType>(*element)) - element = Traversal<Element>::previous(element, stayWithin); - return static_cast<ElementType*>(element); + Node* node = NodeTraversal::previous(current, stayWithin); + while (node && !is<ElementType>(*node)) + node = NodeTraversal::previous(*node, stayWithin); + return downcast<ElementType>(node); } template <typename ElementType> -template <typename CurrentType> -inline ElementType* Traversal<ElementType>::nextSiblingTemplate(CurrentType* current) +inline ElementType* Traversal<ElementType>::nextSibling(const Node& current) { - Node* node = current->nextSibling(); - while (node && !isElementOfType<const ElementType>(*node)) + Node* node = current.nextSibling(); + while (node && !is<ElementType>(*node)) node = node->nextSibling(); - return static_cast<ElementType*>(node); + return downcast<ElementType>(node); } template <typename ElementType> -template <typename CurrentType> -inline ElementType* Traversal<ElementType>::previousSiblingTemplate(CurrentType* current) +inline ElementType* Traversal<ElementType>::previousSibling(const Node& current) { - Node* node = current->previousSibling(); - while (node && !isElementOfType<const ElementType>(*node)) + Node* node = current.previousSibling(); + while (node && !is<ElementType>(*node)) node = node->previousSibling(); - return static_cast<ElementType*>(node); + return downcast<ElementType>(node); } template <typename ElementType> -template <typename CurrentType> -inline ElementType* Traversal<ElementType>::nextSkippingChildrenTemplate(CurrentType* current) +inline ElementType* Traversal<ElementType>::nextSkippingChildren(const Node& current) { Node* node = NodeTraversal::nextSkippingChildren(current); - while (node && !isElementOfType<const ElementType>(*node)) - node = NodeTraversal::nextSkippingChildren(node); - return static_cast<ElementType*>(node); + while (node && !is<ElementType>(*node)) + node = NodeTraversal::nextSkippingChildren(*node); + return downcast<ElementType>(node); } template <typename ElementType> -template <typename CurrentType> -inline ElementType* Traversal<ElementType>::nextSkippingChildrenTemplate(CurrentType* current, const Node* stayWithin) +inline ElementType* Traversal<ElementType>::nextSkippingChildren(const Node& current, const Node* stayWithin) { Node* node = NodeTraversal::nextSkippingChildren(current, stayWithin); - while (node && !isElementOfType<const ElementType>(*node)) - node = NodeTraversal::nextSkippingChildren(node, stayWithin); - return static_cast<ElementType*>(node); + while (node && !is<ElementType>(*node)) + node = NodeTraversal::nextSkippingChildren(*node, stayWithin); + return downcast<ElementType>(node); } template <typename ElementType> -inline ElementType* Traversal<ElementType>::firstChild(const ContainerNode* current) { return firstChildTemplate(current); } +inline ElementType* Traversal<ElementType>::firstChild(const ContainerNode& current) { return firstChildTemplate(current); } template <typename ElementType> -inline ElementType* Traversal<ElementType>::firstChild(const Node* current) { return firstChildTemplate(current); } +inline ElementType* Traversal<ElementType>::firstChild(const Node& current) { return firstChildTemplate(current); } template <typename ElementType> -inline ElementType* Traversal<ElementType>::lastChild(const ContainerNode* current) { return lastChildTemplate(current); } +inline ElementType* Traversal<ElementType>::lastChild(const ContainerNode& current) { return lastChildTemplate(current); } template <typename ElementType> -inline ElementType* Traversal<ElementType>::lastChild(const Node* current) { return lastChildTemplate(current); } +inline ElementType* Traversal<ElementType>::lastChild(const Node& current) { return lastChildTemplate(current); } template <typename ElementType> -inline ElementType* Traversal<ElementType>::firstWithin(const ContainerNode* current) { return firstWithinTemplate(current); } -template <typename ElementType> -inline ElementType* Traversal<ElementType>::firstWithin(const Node* current) { return firstWithinTemplate(current); } +inline ElementType* Traversal<ElementType>::firstWithin(const ContainerNode& current) { return firstWithinTemplate(current); } template <typename ElementType> - -inline ElementType* Traversal<ElementType>::lastWithin(const ContainerNode* current) { return lastWithinTemplate(current); } +inline ElementType* Traversal<ElementType>::firstWithin(const Node& current) { return firstWithinTemplate(current); } template <typename ElementType> -inline ElementType* Traversal<ElementType>::lastWithin(const Node* current) { return lastWithinTemplate(current); } +inline ElementType* Traversal<ElementType>::lastWithin(const ContainerNode& current) { return lastWithinTemplate(current); } template <typename ElementType> -inline ElementType* Traversal<ElementType>::next(const ContainerNode* current) { return nextTemplate(current); } -template <typename ElementType> -inline ElementType* Traversal<ElementType>::next(const Node* current) { return nextTemplate(current); } -template <typename ElementType> -inline ElementType* Traversal<ElementType>::next(const ContainerNode* current, const Node* stayWithin) { return nextTemplate(current, stayWithin); } -template <typename ElementType> -inline ElementType* Traversal<ElementType>::next(const Node* current, const Node* stayWithin) { return nextTemplate(current, stayWithin); } +inline ElementType* Traversal<ElementType>::lastWithin(const Node& current) { return lastWithinTemplate(current); } template <typename ElementType> -inline ElementType* Traversal<ElementType>::previous(const ContainerNode* current) { return previousTemplate(current); } +inline ElementType* Traversal<ElementType>::next(const ContainerNode& current) { return nextTemplate(current); } template <typename ElementType> -inline ElementType* Traversal<ElementType>::previous(const Node* current) { return previousTemplate(current); } +inline ElementType* Traversal<ElementType>::next(const Node& current) { return nextTemplate(current); } template <typename ElementType> -inline ElementType* Traversal<ElementType>::previous(const ContainerNode* current, const Node* stayWithin) { return previousTemplate(current, stayWithin); } +inline ElementType* Traversal<ElementType>::next(const ContainerNode& current, const Node* stayWithin) { return nextTemplate(current, stayWithin); } template <typename ElementType> -inline ElementType* Traversal<ElementType>::previous(const Node* current, const Node* stayWithin) { return previousTemplate(current, stayWithin); } - -template <typename ElementType> -inline ElementType* Traversal<ElementType>::nextSibling(const ContainerNode* current) { return nextSiblingTemplate(current); } -template <typename ElementType> -inline ElementType* Traversal<ElementType>::nextSibling(const Node* current) { return nextSiblingTemplate(current); } - -template <typename ElementType> -inline ElementType* Traversal<ElementType>::previousSibling(const ContainerNode* current) { return previousSiblingTemplate(current); } -template <typename ElementType> -inline ElementType* Traversal<ElementType>::previousSibling(const Node* current) { return previousSiblingTemplate(current); } - -template <typename ElementType> -inline ElementType* Traversal<ElementType>::nextSkippingChildren(const ContainerNode* current) { return nextSkippingChildrenTemplate(current); } -template <typename ElementType> -inline ElementType* Traversal<ElementType>::nextSkippingChildren(const Node* current) { return nextSkippingChildrenTemplate(current); } -template <typename ElementType> -inline ElementType* Traversal<ElementType>::nextSkippingChildren(const ContainerNode* current, const Node* stayWithin) { return nextSkippingChildrenTemplate(current, stayWithin); } -template <typename ElementType> -inline ElementType* Traversal<ElementType>::nextSkippingChildren(const Node* current, const Node* stayWithin) { return nextSkippingChildrenTemplate(current, stayWithin); } +inline ElementType* Traversal<ElementType>::next(const Node& current, const Node* stayWithin) { return nextTemplate(current, stayWithin); } // FIXME: These should go somewhere else. -inline Element* ElementTraversal::previousIncludingPseudo(const Node* current, const Node* stayWithin) +inline Element* ElementTraversal::previousIncludingPseudo(const Node& current, const Node* stayWithin) { Node* node = NodeTraversal::previousIncludingPseudo(current, stayWithin); - while (node && !node->isElementNode()) - node = NodeTraversal::previousIncludingPseudo(node, stayWithin); - return toElement(node); + while (node && !is<Element>(*node)) + node = NodeTraversal::previousIncludingPseudo(*node, stayWithin); + return downcast<Element>(node); } -inline Element* ElementTraversal::nextIncludingPseudo(const Node* current, const Node* stayWithin) +inline Element* ElementTraversal::nextIncludingPseudo(const Node& current, const Node* stayWithin) { Node* node = NodeTraversal::nextIncludingPseudo(current, stayWithin); - while (node && !node->isElementNode()) - node = NodeTraversal::nextIncludingPseudo(node, stayWithin); - return toElement(node); + while (node && !is<Element>(*node)) + node = NodeTraversal::nextIncludingPseudo(*node, stayWithin); + return downcast<Element>(node); } -inline Element* ElementTraversal::nextIncludingPseudoSkippingChildren(const Node* current, const Node* stayWithin) +inline Element* ElementTraversal::nextIncludingPseudoSkippingChildren(const Node& current, const Node* stayWithin) { Node* node = NodeTraversal::nextIncludingPseudoSkippingChildren(current, stayWithin); - while (node && !node->isElementNode()) - node = NodeTraversal::nextIncludingPseudoSkippingChildren(node, stayWithin); - return toElement(node); + while (node && !is<Element>(*node)) + node = NodeTraversal::nextIncludingPseudoSkippingChildren(*node, stayWithin); + return downcast<Element>(node); } -inline Element* ElementTraversal::pseudoAwarePreviousSibling(const Node* current) +inline Element* ElementTraversal::pseudoAwarePreviousSibling(const Node& current) { - Node* node = current->pseudoAwarePreviousSibling(); - while (node && !node->isElementNode()) + Node* node = current.pseudoAwarePreviousSibling(); + while (node && !is<Element>(*node)) node = node->pseudoAwarePreviousSibling(); - return toElement(node); -} - + return downcast<Element>(node); } -#endif +} // namespace WebCore diff --git a/Source/WebCore/dom/Entity.h b/Source/WebCore/dom/Entity.h deleted file mode 100644 index 9ee26a2bf..000000000 --- a/Source/WebCore/dom/Entity.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2000 Peter Kelly (pmk@post.com) - * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef Entity_h -#define Entity_h - -#include "ContainerNode.h" - -namespace WebCore { - -// FIXME: This abstract class is only here so that the JavaScript and Objective-C bindings -// can continue to be compiled. -class Entity : public ContainerNode { -public: - String publicId() const { ASSERT_NOT_REACHED(); return String(); } - String systemId() const { ASSERT_NOT_REACHED(); return String(); } - String notationName() const { ASSERT_NOT_REACHED(); return String(); } - -private: - Entity() : ContainerNode(0) {} -}; - -} //namespace - -#endif diff --git a/Source/WebCore/dom/Entity.idl b/Source/WebCore/dom/Entity.idl deleted file mode 100644 index 7f5bf4ba5..000000000 --- a/Source/WebCore/dom/Entity.idl +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2006 Apple Computer, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ -[ - ImplementationLacksVTable -] interface Entity : Node { - [TreatReturnedNullStringAs=Null] readonly attribute DOMString publicId; - [TreatReturnedNullStringAs=Null] readonly attribute DOMString systemId; - [TreatReturnedNullStringAs=Null] readonly attribute DOMString notationName; -}; - diff --git a/Source/WebCore/dom/EntityReference.cpp b/Source/WebCore/dom/EntityReference.cpp deleted file mode 100644 index 6d93f52d0..000000000 --- a/Source/WebCore/dom/EntityReference.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2000 Peter Kelly (pmk@post.com) - * Copyright (C) 2006, 2008, 2009 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "config.h" -#include "EntityReference.h" - -#include "Document.h" - -namespace WebCore { - -inline EntityReference::EntityReference(Document& document, const String& entityName) - : ContainerNode(&document) - , m_entityName(entityName) -{ -} - -PassRefPtr<EntityReference> EntityReference::create(Document& document, const String& entityName) -{ - return adoptRef(new EntityReference(document, entityName)); -} - -String EntityReference::nodeName() const -{ - return m_entityName; -} - -Node::NodeType EntityReference::nodeType() const -{ - return ENTITY_REFERENCE_NODE; -} - -PassRefPtr<Node> EntityReference::cloneNode(bool) -{ - return create(document(), m_entityName); -} - -} // namespace diff --git a/Source/WebCore/dom/EntityReference.h b/Source/WebCore/dom/EntityReference.h deleted file mode 100644 index 4a64a09e2..000000000 --- a/Source/WebCore/dom/EntityReference.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2000 Peter Kelly (pmk@post.com) - * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef EntityReference_h -#define EntityReference_h - -#include "ContainerNode.h" - -namespace WebCore { - -class EntityReference final : public ContainerNode { -public: - static PassRefPtr<EntityReference> create(Document&, const String& entityName); - -private: - EntityReference(Document&, const String& entityName); - - virtual String nodeName() const override; - virtual NodeType nodeType() const override; - virtual PassRefPtr<Node> cloneNode(bool deep) override; - - String m_entityName; -}; - -} //namespace - -#endif diff --git a/Source/WebCore/dom/EntityReference.idl b/Source/WebCore/dom/EntityReference.idl deleted file mode 100644 index 363554abc..000000000 --- a/Source/WebCore/dom/EntityReference.idl +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -interface EntityReference : Node { -}; - diff --git a/Source/WebCore/dom/ErrorEvent.cpp b/Source/WebCore/dom/ErrorEvent.cpp index d0c337af3..ce7f8ca66 100644 --- a/Source/WebCore/dom/ErrorEvent.cpp +++ b/Source/WebCore/dom/ErrorEvent.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -31,37 +32,32 @@ #include "config.h" #include "ErrorEvent.h" +#include "DOMWrapperWorld.h" #include "EventNames.h" +#include <heap/HeapInlines.h> +#include <heap/StrongInlines.h> -namespace WebCore { +using namespace JSC; -ErrorEventInit::ErrorEventInit() - : message() - , filename() - , lineno(0) - , colno(0) -{ -} - -ErrorEvent::ErrorEvent() -{ -} +namespace WebCore { -ErrorEvent::ErrorEvent(const AtomicString& type, const ErrorEventInit& initializer) - : Event(type, initializer) +ErrorEvent::ErrorEvent(ExecState& state, const AtomicString& type, const Init& initializer, IsTrusted isTrusted) + : Event(type, initializer, isTrusted) , m_message(initializer.message) , m_fileName(initializer.filename) , m_lineNumber(initializer.lineno) , m_columnNumber(initializer.colno) + , m_error(state.vm(), initializer.error) { } -ErrorEvent::ErrorEvent(const String& message, const String& fileName, unsigned lineNumber, unsigned columnNumber) +ErrorEvent::ErrorEvent(const String& message, const String& fileName, unsigned lineNumber, unsigned columnNumber, JSC::Strong<JSC::Unknown> error) : Event(eventNames().errorEvent, false, true) , m_message(message) , m_fileName(fileName) , m_lineNumber(lineNumber) , m_columnNumber(columnNumber) + , m_error(error) { } @@ -74,4 +70,36 @@ EventInterface ErrorEvent::eventInterface() const return ErrorEventInterfaceType; } +JSValue ErrorEvent::error(ExecState& exec, JSGlobalObject& globalObject) +{ + auto error = m_error.get(); + if (!error) + return jsNull(); + + if (error.isObject() && &worldForDOMObject(error.getObject()) != ¤tWorld(&exec)) { + // We need to make sure ErrorEvents do not leak their error property across isolated DOM worlds. + // Ideally, we would check that the worlds have different privileges but that's not possible yet. + auto serializedError = trySerializeError(exec); + if (!serializedError) + return jsNull(); + return serializedError->deserialize(exec, &globalObject); + } + + return error; +} + +RefPtr<SerializedScriptValue> ErrorEvent::trySerializeError(ExecState& exec) +{ + if (!m_triedToSerialize) { + m_serializedDetail = SerializedScriptValue::create(exec, m_error.get(), SerializationErrorMode::NonThrowing); + m_triedToSerialize = true; + } + return m_serializedDetail; +} + +bool ErrorEvent::isErrorEvent() const +{ + return true; +} + } // namespace WebCore diff --git a/Source/WebCore/dom/ErrorEvent.h b/Source/WebCore/dom/ErrorEvent.h index d58eed6a8..0c865e8d9 100644 --- a/Source/WebCore/dom/ErrorEvent.h +++ b/Source/WebCore/dom/ErrorEvent.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -28,57 +29,62 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ErrorEvent_h -#define ErrorEvent_h +#pragma once #include "Event.h" +#include "SerializedScriptValue.h" +#include <heap/Strong.h> #include <wtf/text/WTFString.h> namespace WebCore { -struct ErrorEventInit : public EventInit { - ErrorEventInit(); - - String message; - String filename; - unsigned lineno; - unsigned colno; -}; - -class ErrorEvent : public Event { +class ErrorEvent final : public Event { public: - static PassRefPtr<ErrorEvent> create() - { - return adoptRef(new ErrorEvent); - } - static PassRefPtr<ErrorEvent> create(const String& message, const String& fileName, unsigned lineNumber, unsigned columnNumber) + static Ref<ErrorEvent> create(const String& message, const String& fileName, unsigned lineNumber, unsigned columnNumber, JSC::Strong<JSC::Unknown> error) { - return adoptRef(new ErrorEvent(message, fileName, lineNumber, columnNumber)); + return adoptRef(*new ErrorEvent(message, fileName, lineNumber, columnNumber, error)); } - static PassRefPtr<ErrorEvent> create(const AtomicString& type, const ErrorEventInit& initializer) + + struct Init : EventInit { + String message; + String filename; + unsigned lineno { 0 }; + unsigned colno { 0 }; + JSC::JSValue error; + }; + + static Ref<ErrorEvent> create(JSC::ExecState& state, const AtomicString& type, const Init& initializer, IsTrusted isTrusted = IsTrusted::No) { - return adoptRef(new ErrorEvent(type, initializer)); + return adoptRef(*new ErrorEvent(state, type, initializer, isTrusted)); } + virtual ~ErrorEvent(); const String& message() const { return m_message; } const String& filename() const { return m_fileName; } unsigned lineno() const { return m_lineNumber; } unsigned colno() const { return m_columnNumber; } + JSC::JSValue error(JSC::ExecState&, JSC::JSGlobalObject&); - virtual EventInterface eventInterface() const override; + EventInterface eventInterface() const override; private: - ErrorEvent(); - ErrorEvent(const String& message, const String& fileName, unsigned lineNumber, unsigned columnNumber); - ErrorEvent(const AtomicString&, const ErrorEventInit&); + ErrorEvent(const String& message, const String& fileName, unsigned lineNumber, unsigned columnNumber, JSC::Strong<JSC::Unknown> error); + ErrorEvent(JSC::ExecState&, const AtomicString&, const Init&, IsTrusted); + + RefPtr<SerializedScriptValue> trySerializeError(JSC::ExecState&); + + bool isErrorEvent() const override; String m_message; String m_fileName; unsigned m_lineNumber; unsigned m_columnNumber; + JSC::Strong<JSC::Unknown> m_error; + RefPtr<SerializedScriptValue> m_serializedDetail; + bool m_triedToSerialize { false }; }; } // namespace WebCore -#endif // ErrorEvent_h +SPECIALIZE_TYPE_TRAITS_EVENT(ErrorEvent) diff --git a/Source/WebCore/dom/ErrorEvent.idl b/Source/WebCore/dom/ErrorEvent.idl index 1c7bb7966..edb5e4b87 100644 --- a/Source/WebCore/dom/ErrorEvent.idl +++ b/Source/WebCore/dom/ErrorEvent.idl @@ -1,5 +1,6 @@ /* * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 2016 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -29,12 +30,21 @@ */ [ - JSNoStaticTables, - ConstructorTemplate=Event + Constructor(DOMString type, optional ErrorEventInit eventInitDict), + ConstructorCallWith=ScriptState, + Exposed=(Window,Worker), ] interface ErrorEvent : Event { - [InitializedByEventConstructor] readonly attribute DOMString message; - [InitializedByEventConstructor] readonly attribute DOMString filename; - [InitializedByEventConstructor] readonly attribute unsigned long lineno; - [InitializedByEventConstructor] readonly attribute unsigned long colno; + readonly attribute DOMString message; + readonly attribute USVString filename; + readonly attribute unsigned long lineno; + readonly attribute unsigned long colno; + [CallWith=ScriptState&GlobalObject] readonly attribute any error; }; +dictionary ErrorEventInit : EventInit { + DOMString message = ""; + USVString filename = ""; + unsigned long lineno = 0; + unsigned long colno = 0; + any error = null; +}; diff --git a/Source/WebCore/dom/Event.cpp b/Source/WebCore/dom/Event.cpp index e222072d8..870595184 100644 --- a/Source/WebCore/dom/Event.cpp +++ b/Source/WebCore/dom/Event.cpp @@ -23,79 +23,47 @@ #include "config.h" #include "Event.h" +#include "EventNames.h" +#include "EventPath.h" #include "EventTarget.h" #include "UserGestureIndicator.h" #include <wtf/CurrentTime.h> namespace WebCore { -EventInit::EventInit() - : bubbles(false) - , cancelable(false) -{ -} - -EventInit::EventInit(bool b, bool c) - : bubbles(b) - , cancelable(c) -{ -} - -Event::Event() - : m_canBubble(false) - , m_cancelable(false) - , m_propagationStopped(false) - , m_immediatePropagationStopped(false) - , m_defaultPrevented(false) - , m_defaultHandled(false) - , m_cancelBubble(false) - , m_eventPhase(0) - , m_currentTarget(0) +Event::Event(IsTrusted isTrusted) + : m_isTrusted(isTrusted == IsTrusted::Yes) , m_createTime(convertSecondsToDOMTimeStamp(currentTime())) { } Event::Event(const AtomicString& eventType, bool canBubbleArg, bool cancelableArg) : m_type(eventType) + , m_isInitialized(true) , m_canBubble(canBubbleArg) , m_cancelable(cancelableArg) - , m_propagationStopped(false) - , m_immediatePropagationStopped(false) - , m_defaultPrevented(false) - , m_defaultHandled(false) - , m_cancelBubble(false) - , m_eventPhase(0) - , m_currentTarget(0) + , m_isTrusted(true) , m_createTime(convertSecondsToDOMTimeStamp(currentTime())) { } Event::Event(const AtomicString& eventType, bool canBubbleArg, bool cancelableArg, double timestamp) : m_type(eventType) + , m_isInitialized(true) , m_canBubble(canBubbleArg) , m_cancelable(cancelableArg) - , m_propagationStopped(false) - , m_immediatePropagationStopped(false) - , m_defaultPrevented(false) - , m_defaultHandled(false) - , m_cancelBubble(false) - , m_eventPhase(0) - , m_currentTarget(0) + , m_isTrusted(true) , m_createTime(convertSecondsToDOMTimeStamp(timestamp)) { } -Event::Event(const AtomicString& eventType, const EventInit& initializer) +Event::Event(const AtomicString& eventType, const EventInit& initializer, IsTrusted isTrusted) : m_type(eventType) + , m_isInitialized(true) , m_canBubble(initializer.bubbles) , m_cancelable(initializer.cancelable) - , m_propagationStopped(false) - , m_immediatePropagationStopped(false) - , m_defaultPrevented(false) - , m_defaultHandled(false) - , m_cancelBubble(false) - , m_eventPhase(0) - , m_currentTarget(0) + , m_composed(initializer.composed) + , m_isTrusted(isTrusted == IsTrusted::Yes) , m_createTime(convertSecondsToDOMTimeStamp(currentTime())) { } @@ -106,18 +74,42 @@ Event::~Event() void Event::initEvent(const AtomicString& eventTypeArg, bool canBubbleArg, bool cancelableArg) { - if (dispatched()) + if (isBeingDispatched()) return; + m_isInitialized = true; m_propagationStopped = false; m_immediatePropagationStopped = false; m_defaultPrevented = false; + m_isTrusted = false; + m_target = nullptr; m_type = eventTypeArg; m_canBubble = canBubbleArg; m_cancelable = cancelableArg; } +bool Event::composed() const +{ + if (m_composed) + return true; + + // http://w3c.github.io/webcomponents/spec/shadow/#scoped-flag + if (!isTrusted()) + return false; + + return m_type == eventNames().inputEvent + || m_type == eventNames().textInputEvent + || m_type == eventNames().DOMActivateEvent + || isCompositionEvent() + || isClipboardEvent() + || isFocusEvent() + || isKeyboardEvent() + || isMouseEvent() + || isTouchEvent() + || isInputEvent(); +} + EventInterface Event::eventInterface() const { return EventInterfaceType; @@ -143,12 +135,17 @@ bool Event::isKeyboardEvent() const return false; } -bool Event::isTouchEvent() const +bool Event::isInputEvent() const +{ + return false; +} + +bool Event::isCompositionEvent() const { return false; } -bool Event::isDragEvent() const +bool Event::isTouchEvent() const { return false; } @@ -168,32 +165,55 @@ bool Event::isBeforeUnloadEvent() const return false; } -PassRefPtr<Event> Event::cloneFor(HTMLIFrameElement*) const +bool Event::isErrorEvent() const +{ + return false; +} + +bool Event::isTextEvent() const +{ + return false; +} + +bool Event::isWheelEvent() const { - return Event::create(type(), bubbles(), cancelable()); + return false; } -void Event::setTarget(PassRefPtr<EventTarget> target) +void Event::setTarget(RefPtr<EventTarget>&& target) { if (m_target == target) return; - m_target = target; + m_target = WTFMove(target); if (m_target) receivedTarget(); } +void Event::setCurrentTarget(EventTarget* currentTarget) +{ + m_currentTarget = currentTarget; +} + +Vector<EventTarget*> Event::composedPath() const +{ + if (!m_eventPath) + return Vector<EventTarget*>(); + return m_eventPath->computePathUnclosedToTarget(*m_currentTarget); +} + void Event::receivedTarget() { } -void Event::setUnderlyingEvent(PassRefPtr<Event> ue) +void Event::setUnderlyingEvent(Event* underlyingEvent) { // Prohibit creation of a cycle -- just do nothing in that case. - for (Event* e = ue.get(); e; e = e->underlyingEvent()) - if (e == this) + for (Event* event = underlyingEvent; event; event = event->underlyingEvent()) { + if (event == this) return; - m_underlyingEvent = ue; + } + m_underlyingEvent = underlyingEvent; } } // namespace WebCore diff --git a/Source/WebCore/dom/Event.h b/Source/WebCore/dom/Event.h index 9cd6dcddd..9edb3af31 100644 --- a/Source/WebCore/dom/Event.h +++ b/Source/WebCore/dom/Event.h @@ -21,30 +21,24 @@ * */ -#ifndef Event_h -#define Event_h +#pragma once #include "DOMTimeStamp.h" +#include "EventInit.h" #include "EventInterfaces.h" +#include "ExceptionOr.h" #include "ScriptWrappable.h" -#include <wtf/HashMap.h> -#include <wtf/ListHashSet.h> #include <wtf/RefCounted.h> +#include <wtf/TypeCasts.h> #include <wtf/text/AtomicString.h> namespace WebCore { -class Clipboard; +class DataTransfer; +class EventPath; class EventTarget; class HTMLIFrameElement; - -struct EventInit { - EventInit(); - EventInit(bool bubbles, bool cancelable); - - bool bubbles; - bool cancelable; -}; +class ScriptExecutionContext; enum EventInterface { @@ -56,6 +50,8 @@ DOM_EVENT_INTERFACES_FOR_EACH(DOM_EVENT_INTERFACE_DECLARE) class Event : public ScriptWrappable, public RefCounted<Event> { public: + enum class IsTrusted { No, Yes }; + enum PhaseType { NONE = 0, CAPTURING_PHASE = 1, @@ -63,61 +59,54 @@ public: BUBBLING_PHASE = 3 }; - enum EventType { - MOUSEDOWN = 1, - MOUSEUP = 2, - MOUSEOVER = 4, - MOUSEOUT = 8, - MOUSEMOVE = 16, - MOUSEDRAG = 32, - CLICK = 64, - DBLCLICK = 128, - KEYDOWN = 256, - KEYUP = 512, - KEYPRESS = 1024, - DRAGDROP = 2048, - FOCUS = 4096, - BLUR = 8192, - SELECT = 16384, - CHANGE = 32768 - }; - - static PassRefPtr<Event> create() + static Ref<Event> create(const AtomicString& type, bool canBubble, bool cancelable) { - return adoptRef(new Event); + return adoptRef(*new Event(type, canBubble, cancelable)); } - static PassRefPtr<Event> create(const AtomicString& type, bool canBubble, bool cancelable) + + static Ref<Event> createForBindings() { - return adoptRef(new Event(type, canBubble, cancelable)); + return adoptRef(*new Event); } - static PassRefPtr<Event> create(const AtomicString& type, const EventInit& initializer) + static Ref<Event> create(const AtomicString& type, const EventInit& initializer, IsTrusted isTrusted = IsTrusted::No) { - return adoptRef(new Event(type, initializer)); + return adoptRef(*new Event(type, initializer, isTrusted)); } virtual ~Event(); - void initEvent(const AtomicString& type, bool canBubble, bool cancelable); + WEBCORE_EXPORT void initEvent(const AtomicString& type, bool canBubble, bool cancelable); + + bool isInitialized() const { return m_isInitialized; } const AtomicString& type() const { return m_type; } void setType(const AtomicString& type) { m_type = type; } EventTarget* target() const { return m_target.get(); } - void setTarget(PassRefPtr<EventTarget>); + void setTarget(RefPtr<EventTarget>&&); - EventTarget* currentTarget() const { return m_currentTarget; } - void setCurrentTarget(EventTarget* currentTarget) { m_currentTarget = currentTarget; } + EventTarget* currentTarget() const { return m_currentTarget.get(); } + void setCurrentTarget(EventTarget*); unsigned short eventPhase() const { return m_eventPhase; } void setEventPhase(unsigned short eventPhase) { m_eventPhase = eventPhase; } bool bubbles() const { return m_canBubble; } bool cancelable() const { return m_cancelable; } + WEBCORE_EXPORT bool composed() const; + DOMTimeStamp timeStamp() const { return m_createTime; } + void setEventPath(const EventPath& path) { m_eventPath = &path; } + void clearEventPath() { m_eventPath = nullptr; } + Vector<EventTarget*> composedPath() const; + void stopPropagation() { m_propagationStopped = true; } void stopImmediatePropagation() { m_immediatePropagationStopped = true; } + + bool isTrusted() const { return m_isTrusted; } + void setUntrusted() { m_isTrusted = false; } // IE Extensions EventTarget* srcElement() const { return target(); } // MSIE extension - "the object that fired the event" @@ -125,8 +114,6 @@ public: bool legacyReturnValue() const { return !defaultPrevented(); } void setLegacyReturnValue(bool returnValue) { setDefaultPrevented(!returnValue); } - Clipboard* clipboardData() const { return isClipboardEvent() ? clipboard() : 0; } - virtual EventInterface eventInterface() const; // These events are general classes of events. @@ -134,24 +121,33 @@ public: virtual bool isMouseEvent() const; virtual bool isFocusEvent() const; virtual bool isKeyboardEvent() const; + virtual bool isInputEvent() const; + virtual bool isCompositionEvent() const; virtual bool isTouchEvent() const; - // Drag events are a subset of mouse events. - virtual bool isDragEvent() const; - // These events lack a DOM interface. virtual bool isClipboardEvent() const; virtual bool isBeforeTextInsertedEvent() const; virtual bool isBeforeUnloadEvent() const; + virtual bool isErrorEvent() const; + virtual bool isTextEvent() const; + virtual bool isWheelEvent() const; + +#if ENABLE(INDEXED_DATABASE) + virtual bool isVersionChangeEvent() const { return false; } +#endif + bool propagationStopped() const { return m_propagationStopped || m_immediatePropagationStopped; } bool immediatePropagationStopped() const { return m_immediatePropagationStopped; } + void resetPropagationFlags(); + bool defaultPrevented() const { return m_defaultPrevented; } void preventDefault() { - if (m_cancelable) + if (m_cancelable && !m_isExecutingPassiveEventListener) m_defaultPrevented = true; } void setDefaultPrevented(bool defaultPrevented) { m_defaultPrevented = defaultPrevented; } @@ -159,48 +155,66 @@ public: bool defaultHandled() const { return m_defaultHandled; } void setDefaultHandled() { m_defaultHandled = true; } - bool cancelBubble() const { return m_cancelBubble; } - void setCancelBubble(bool cancel) { m_cancelBubble = cancel; } + void setInPassiveListener(bool value) { m_isExecutingPassiveEventListener = value; } - Event* underlyingEvent() const { return m_underlyingEvent.get(); } - void setUnderlyingEvent(PassRefPtr<Event>); + bool cancelBubble() const { return propagationStopped(); } + void setCancelBubble(bool); - virtual Clipboard* clipboard() const { return 0; } + Event* underlyingEvent() const { return m_underlyingEvent.get(); } + void setUnderlyingEvent(Event*); bool isBeingDispatched() const { return eventPhase(); } - virtual PassRefPtr<Event> cloneFor(HTMLIFrameElement*) const; - virtual EventTarget* relatedTarget() const { return nullptr; } protected: - Event(); - Event(const AtomicString& type, bool canBubble, bool cancelable); + Event(IsTrusted = IsTrusted::No); + WEBCORE_EXPORT Event(const AtomicString& type, bool canBubble, bool cancelable); Event(const AtomicString& type, bool canBubble, bool cancelable, double timestamp); - Event(const AtomicString& type, const EventInit&); + Event(const AtomicString& type, const EventInit&, IsTrusted); virtual void receivedTarget(); bool dispatched() const { return m_target; } private: AtomicString m_type; - bool m_canBubble; - bool m_cancelable; - - bool m_propagationStopped; - bool m_immediatePropagationStopped; - bool m_defaultPrevented; - bool m_defaultHandled; - bool m_cancelBubble; - unsigned short m_eventPhase; - EventTarget* m_currentTarget; + bool m_isInitialized { false }; + bool m_canBubble { false }; + bool m_cancelable { false }; + bool m_composed { false }; + + bool m_propagationStopped { false }; + bool m_immediatePropagationStopped { false }; + bool m_defaultPrevented { false }; + bool m_defaultHandled { false }; + bool m_isTrusted { false }; + bool m_isExecutingPassiveEventListener { false }; + + unsigned short m_eventPhase { 0 }; + RefPtr<EventTarget> m_currentTarget; + const EventPath* m_eventPath { nullptr }; RefPtr<EventTarget> m_target; DOMTimeStamp m_createTime; RefPtr<Event> m_underlyingEvent; }; +inline void Event::resetPropagationFlags() +{ + m_propagationStopped = false; + m_immediatePropagationStopped = false; +} + +inline void Event::setCancelBubble(bool cancel) +{ + if (cancel) + m_propagationStopped = true; +} + } // namespace WebCore -#endif // Event_h +#define SPECIALIZE_TYPE_TRAITS_EVENT(ToValueTypeName) \ +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ToValueTypeName) \ + static bool isType(const WebCore::Event& event) { return event.is##ToValueTypeName(); } \ +SPECIALIZE_TYPE_TRAITS_END() diff --git a/Source/WebCore/dom/Event.idl b/Source/WebCore/dom/Event.idl index bfb221e03..70f9bfeb3 100644 --- a/Source/WebCore/dom/Event.idl +++ b/Source/WebCore/dom/Event.idl @@ -18,72 +18,45 @@ * Boston, MA 02110-1301, USA. */ -// Introduced in DOM Level 2: +// FIXME: This should be an implementation provided typedef +// https://heycam.github.io/webidl/#DOMTimeStamp +typedef unsigned long long DOMTimeStamp; + [ + Constructor(DOMString type, optional EventInit eventInitDict), CustomToJSObject, - ConstructorTemplate=Event, - JSNoStaticTables, - ObjCPolymorphic, + ExportToWrappedFunction, + Exposed=(Window,Worker), + JSCustomHeader, ] interface Event { + // PhaseType + const unsigned short NONE = 0; + const unsigned short CAPTURING_PHASE = 1; + const unsigned short AT_TARGET = 2; + const unsigned short BUBBLING_PHASE = 3; - // DOM PhaseType - const unsigned short NONE = 0; - const unsigned short CAPTURING_PHASE = 1; - const unsigned short AT_TARGET = 2; - const unsigned short BUBBLING_PHASE = 3; + readonly attribute DOMString type; + readonly attribute EventTarget target; + readonly attribute EventTarget currentTarget; + readonly attribute unsigned short eventPhase; + readonly attribute boolean bubbles; + readonly attribute boolean cancelable; + [EnabledAtRuntime=ShadowDOM] readonly attribute boolean composed; + readonly attribute DOMTimeStamp timeStamp; -#if !defined(LANGUAGE_OBJECTIVE_C) || !LANGUAGE_OBJECTIVE_C - // Reverse-engineered from Netscape - const unsigned short MOUSEDOWN = 1; - const unsigned short MOUSEUP = 2; - const unsigned short MOUSEOVER = 4; - const unsigned short MOUSEOUT = 8; - const unsigned short MOUSEMOVE = 16; - const unsigned short MOUSEDRAG = 32; - const unsigned short CLICK = 64; - const unsigned short DBLCLICK = 128; - const unsigned short KEYDOWN = 256; - const unsigned short KEYUP = 512; - const unsigned short KEYPRESS = 1024; - const unsigned short DRAGDROP = 2048; - const unsigned short FOCUS = 4096; - const unsigned short BLUR = 8192; - const unsigned short SELECT = 16384; - const unsigned short CHANGE = 32768; -#endif + [EnabledAtRuntime=ShadowDOM] sequence<Node> composedPath(); - readonly attribute DOMString type; - readonly attribute EventTarget target; - readonly attribute EventTarget currentTarget; - readonly attribute unsigned short eventPhase; - [InitializedByEventConstructor] readonly attribute boolean bubbles; - [InitializedByEventConstructor] readonly attribute boolean cancelable; - readonly attribute DOMTimeStamp timeStamp; + void stopPropagation(); + void preventDefault(); - void stopPropagation(); - void preventDefault(); - [ObjCLegacyUnnamedParameters] void initEvent([Default=Undefined] optional DOMString eventTypeArg, - [Default=Undefined] optional boolean canBubbleArg, - [Default=Undefined] optional boolean cancelableArg); + void initEvent(DOMString type, optional boolean bubbles = false, optional boolean cancelable = false); - // DOM Level 3 Additions. readonly attribute boolean defaultPrevented; void stopImmediatePropagation(); - // IE Extensions - readonly attribute EventTarget srcElement; - [ImplementedAs=legacyReturnValue] attribute boolean returnValue; - attribute boolean cancelBubble; - -#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT - [Custom] readonly attribute Clipboard clipboardData; -#endif - -#if defined(LANGUAGE_CPP) && LANGUAGE_CPP - // Extra WebCore methods exposed to allow compile-time casting in C++ - boolean isMouseEvent(); - boolean isUIEvent(); -#endif + [Unforgeable] readonly attribute boolean isTrusted; + readonly attribute EventTarget srcElement; + [ImplementedAs=legacyReturnValue] attribute boolean returnValue; + attribute boolean cancelBubble; }; - diff --git a/Source/WebCore/dom/EventContext.cpp b/Source/WebCore/dom/EventContext.cpp index 5c1feb171..8fc38a360 100644 --- a/Source/WebCore/dom/EventContext.cpp +++ b/Source/WebCore/dom/EventContext.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -34,7 +34,7 @@ namespace WebCore { -EventContext::EventContext(PassRefPtr<Node> node, PassRefPtr<EventTarget> currentTarget, PassRefPtr<EventTarget> target) +EventContext::EventContext(Node* node, EventTarget* currentTarget, EventTarget* target) : m_node(node) , m_currentTarget(currentTarget) , m_target(target) @@ -64,9 +64,8 @@ bool EventContext::isTouchEventContext() const return false; } -MouseOrFocusEventContext::MouseOrFocusEventContext(PassRefPtr<Node> node, PassRefPtr<EventTarget> currentTarget, PassRefPtr<EventTarget> target) +MouseOrFocusEventContext::MouseOrFocusEventContext(Node* node, EventTarget* currentTarget, EventTarget* target) : EventContext(node, currentTarget, target) - , m_relatedTarget(0) { } @@ -76,12 +75,12 @@ MouseOrFocusEventContext::~MouseOrFocusEventContext() void MouseOrFocusEventContext::handleLocalEvents(Event& event) const { - ASSERT(event.isMouseEvent() || event.isFocusEvent()); + ASSERT(is<MouseEvent>(event) || is<FocusEvent>(event)); if (m_relatedTarget) { - if (event.isMouseEvent()) - toMouseEvent(event).setRelatedTarget(m_relatedTarget.get()); - else if (event.isFocusEvent()) - toFocusEvent(event).setRelatedTarget(m_relatedTarget.get()); + if (is<MouseEvent>(event)) + downcast<MouseEvent>(event).setRelatedTarget(m_relatedTarget.get()); + else if (is<FocusEvent>(event)) + downcast<FocusEvent>(event).setRelatedTarget(m_relatedTarget.get()); } EventContext::handleLocalEvents(event); } @@ -91,8 +90,9 @@ bool MouseOrFocusEventContext::isMouseOrFocusEventContext() const return true; } -#if ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS) -TouchEventContext::TouchEventContext(PassRefPtr<Node> node, PassRefPtr<EventTarget> currentTarget, PassRefPtr<EventTarget> target) +#if ENABLE(TOUCH_EVENTS) + +TouchEventContext::TouchEventContext(Node* node, EventTarget* currentTarget, EventTarget* target) : EventContext(node, currentTarget, target) , m_touches(TouchList::create()) , m_targetTouches(TouchList::create()) @@ -106,16 +106,16 @@ TouchEventContext::~TouchEventContext() void TouchEventContext::handleLocalEvents(Event& event) const { -#ifndef NDEBUG +#if !ASSERT_DISABLED checkReachability(m_touches.get()); checkReachability(m_targetTouches.get()); checkReachability(m_changedTouches.get()); #endif - ASSERT(event.isTouchEvent()); - TouchEvent& touchEvent = toTouchEvent(event); - touchEvent.setTouches(m_touches); - touchEvent.setTargetTouches(m_targetTouches); - touchEvent.setChangedTouches(m_changedTouches); + ASSERT(is<TouchEvent>(event)); + TouchEvent& touchEvent = downcast<TouchEvent>(event); + touchEvent.setTouches(m_touches.get()); + touchEvent.setTargetTouches(m_targetTouches.get()); + touchEvent.setChangedTouches(m_changedTouches.get()); EventContext::handleLocalEvents(event); } @@ -124,12 +124,15 @@ bool TouchEventContext::isTouchEventContext() const return true; } -#ifndef NDEBUG +#if !ASSERT_DISABLED + void TouchEventContext::checkReachability(TouchList* touchList) const { - for (size_t i = 0; i < touchList->length(); ++i) - ASSERT(isReachable(touchList->item(i)->target()->toNode())); + size_t length = touchList->length(); + for (size_t i = 0; i < length; ++i) + ASSERT(!isUnreachableNode(touchList->item(i)->target()->toNode())); } + #endif #endif // ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS) diff --git a/Source/WebCore/dom/EventContext.h b/Source/WebCore/dom/EventContext.h index fd0287d28..0ccebe17f 100644 --- a/Source/WebCore/dom/EventContext.h +++ b/Source/WebCore/dom/EventContext.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,28 +24,24 @@ * */ -#ifndef EventContext_h -#define EventContext_h +#pragma once -#include "EventTarget.h" #include "Node.h" #include "TreeScope.h" -#include <wtf/RefPtr.h> namespace WebCore { -class Event; -#if ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS) class TouchList; -#endif class EventContext { + WTF_MAKE_FAST_ALLOCATED; public: // FIXME: Use ContainerNode instead of Node. - EventContext(PassRefPtr<Node>, PassRefPtr<EventTarget> currentTarget, PassRefPtr<EventTarget> target); + EventContext(Node*, EventTarget* currentTarget, EventTarget*); virtual ~EventContext(); Node* node() const { return m_node.get(); } + EventTarget* currentTarget() const { return m_currentTarget.get(); } EventTarget* target() const { return m_target.get(); } bool currentTargetSameAsTarget() const { return m_currentTarget.get() == m_target.get(); } virtual void handleLocalEvents(Event&) const; @@ -53,10 +49,10 @@ public: virtual bool isTouchEventContext() const; protected: -#ifndef NDEBUG - bool isUnreachableNode(EventTarget*); - bool isReachable(Node*) const; +#if !ASSERT_DISABLED + bool isUnreachableNode(EventTarget*) const; #endif + RefPtr<Node> m_node; RefPtr<EventTarget> m_currentTarget; RefPtr<EventTarget> m_target; @@ -64,101 +60,92 @@ protected: class MouseOrFocusEventContext final : public EventContext { public: - MouseOrFocusEventContext(PassRefPtr<Node>, PassRefPtr<EventTarget> currentTarget, PassRefPtr<EventTarget> target); + MouseOrFocusEventContext(Node*, EventTarget* currentTarget, EventTarget*); virtual ~MouseOrFocusEventContext(); - EventTarget* relatedTarget() const { return m_relatedTarget.get(); } - void setRelatedTarget(PassRefPtr<EventTarget>); - virtual void handleLocalEvents(Event&) const override; - virtual bool isMouseOrFocusEventContext() const override; + + Node* relatedTarget() const { return m_relatedTarget.get(); } + void setRelatedTarget(Node*); private: - RefPtr<EventTarget> m_relatedTarget; -}; + void handleLocalEvents(Event&) const final; + bool isMouseOrFocusEventContext() const final; -inline MouseOrFocusEventContext& toMouseOrFocusEventContext(EventContext& eventContext) -{ - ASSERT_WITH_SECURITY_IMPLICATION(eventContext.isMouseOrFocusEventContext()); - return static_cast<MouseOrFocusEventContext&>(eventContext); -} + RefPtr<Node> m_relatedTarget; +}; +#if ENABLE(TOUCH_EVENTS) -#if ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS) class TouchEventContext final : public EventContext { public: - TouchEventContext(PassRefPtr<Node>, PassRefPtr<EventTarget> currentTarget, PassRefPtr<EventTarget> target); + TouchEventContext(Node*, EventTarget* currentTarget, EventTarget*); virtual ~TouchEventContext(); - virtual void handleLocalEvents(Event&) const override; - virtual bool isTouchEventContext() const override; + void handleLocalEvents(Event&) const override; + bool isTouchEventContext() const override; enum TouchListType { Touches, TargetTouches, ChangedTouches, NotTouchList }; - TouchList* touchList(TouchListType type) - { - switch (type) { - case Touches: - return m_touches.get(); - case TargetTouches: - return m_targetTouches.get(); - case ChangedTouches: - return m_changedTouches.get(); - case NotTouchList: - break; - } - ASSERT_NOT_REACHED(); - return nullptr; - } + TouchList* touchList(TouchListType); TouchList* touches() { return m_touches.get(); } TouchList* targetTouches() { return m_targetTouches.get(); } TouchList* changedTouches() { return m_changedTouches.get(); } private: +#if !ASSERT_DISABLED + void checkReachability(TouchList*) const; +#endif + RefPtr<TouchList> m_touches; RefPtr<TouchList> m_targetTouches; RefPtr<TouchList> m_changedTouches; -#ifndef NDEBUG - void checkReachability(TouchList*) const; -#endif }; -inline TouchEventContext& toTouchEventContext(EventContext& eventContext) -{ - ASSERT_WITH_SECURITY_IMPLICATION(eventContext.isTouchEventContext()); - return static_cast<TouchEventContext&>(eventContext); -} +#endif // ENABLE(TOUCH_EVENTS) -inline TouchEventContext* toTouchEventContext(EventContext* eventContext) -{ - ASSERT_WITH_SECURITY_IMPLICATION(!eventContext || eventContext->isTouchEventContext()); - return static_cast<TouchEventContext*>(eventContext); -} -#endif // ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS) +#if !ASSERT_DISABLED -#ifndef NDEBUG -inline bool EventContext::isUnreachableNode(EventTarget* target) +inline bool EventContext::isUnreachableNode(EventTarget* target) const { // FIXME: Checks also for SVG elements. - return target && target->toNode() && !target->toNode()->isSVGElement() && !isReachable(target->toNode()); + return target && target->toNode() && !target->toNode()->isSVGElement() && m_node->isClosedShadowHidden(*target->toNode()); } -inline bool EventContext::isReachable(Node* target) const -{ - ASSERT(target); - TreeScope& targetScope = target->treeScope(); - for (TreeScope* scope = &m_node->treeScope(); scope; scope = scope->parentTreeScope()) { - if (scope == &targetScope) - return true; - } - return false; -} #endif -inline void MouseOrFocusEventContext::setRelatedTarget(PassRefPtr<EventTarget> relatedTarget) +inline void MouseOrFocusEventContext::setRelatedTarget(Node* relatedTarget) { - ASSERT(!isUnreachableNode(relatedTarget.get())); + ASSERT(!isUnreachableNode(relatedTarget)); m_relatedTarget = relatedTarget; } +#if ENABLE(TOUCH_EVENTS) + +inline TouchList* TouchEventContext::touchList(TouchListType type) +{ + switch (type) { + case Touches: + return m_touches.get(); + case TargetTouches: + return m_targetTouches.get(); + case ChangedTouches: + return m_changedTouches.get(); + case NotTouchList: + break; + } + ASSERT_NOT_REACHED(); + return nullptr; } -#endif // EventContext_h +#endif + +} // namespace WebCore + +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::MouseOrFocusEventContext) +static bool isType(const WebCore::EventContext& context) { return context.isMouseOrFocusEventContext(); } +SPECIALIZE_TYPE_TRAITS_END() + +#if ENABLE(TOUCH_EVENTS) +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::TouchEventContext) +static bool isType(const WebCore::EventContext& context) { return context.isTouchEventContext(); } +SPECIALIZE_TYPE_TRAITS_END() +#endif diff --git a/Source/WebCore/dom/EventDispatcher.cpp b/Source/WebCore/dom/EventDispatcher.cpp index b61d440ae..0c3facc3a 100644 --- a/Source/WebCore/dom/EventDispatcher.cpp +++ b/Source/WebCore/dom/EventDispatcher.cpp @@ -2,7 +2,7 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2004-2016 Apple Inc. All rights reserved. * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * Copyright (C) 2010, 2011, 2012, 2013 Google Inc. All rights reserved. @@ -26,217 +26,38 @@ #include "config.h" #include "EventDispatcher.h" +#include "CompositionEvent.h" #include "EventContext.h" -#include "FocusEvent.h" +#include "EventPath.h" +#include "Frame.h" #include "FrameView.h" #include "HTMLInputElement.h" -#include "HTMLMediaElement.h" -#include "InsertionPoint.h" -#include "InspectorInstrumentation.h" +#include "InputEvent.h" +#include "KeyboardEvent.h" +#include "MainFrame.h" #include "MouseEvent.h" -#include "PseudoElement.h" +#include "NoEventDispatchAssertion.h" #include "ScopedEventQueue.h" #include "ShadowRoot.h" +#include "TextEvent.h" #include "TouchEvent.h" -#if ENABLE(SVG) -#include "SVGElementInstance.h" -#include "SVGNames.h" -#include "SVGUseElement.h" -#endif - namespace WebCore { -class WindowEventContext { -public: - WindowEventContext(PassRefPtr<Node>, const EventContext*); - - DOMWindow* window() const { return m_window.get(); } - EventTarget* target() const { return m_target.get(); } - bool handleLocalEvents(Event&); - -private: - RefPtr<DOMWindow> m_window; - RefPtr<EventTarget> m_target; -}; - -WindowEventContext::WindowEventContext(PassRefPtr<Node> node, const EventContext* topEventContext) -{ - Node* topLevelContainer = topEventContext ? topEventContext->node() : node.get(); - if (!topLevelContainer->isDocumentNode()) - return; - - m_window = toDocument(topLevelContainer)->domWindow(); - m_target = topEventContext ? topEventContext->target() : node.get(); -} - -bool WindowEventContext::handleLocalEvents(Event& event) -{ - if (!m_window) - return false; - - event.setTarget(m_target.get()); - event.setCurrentTarget(m_window.get()); - m_window->fireEventListeners(&event); - return true; -} - -class EventPath { -public: - EventPath(Node& origin, Event&); - - bool isEmpty() const { return m_path.isEmpty(); } - size_t size() const { return m_path.size(); } - const EventContext& contextAt(size_t i) const { return *m_path[i]; } - EventContext& contextAt(size_t i) { return *m_path[i]; } - -#if ENABLE(TOUCH_EVENTS) - bool updateTouchLists(const TouchEvent&); -#endif - void setRelatedTarget(EventTarget&); - - bool hasEventListeners(const AtomicString& eventType) const; - - EventContext* lastContextIfExists() { return m_path.isEmpty() ? 0 : m_path.last().get(); } - -private: -#if ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS) - void updateTouchListsInEventPath(const TouchList*, TouchEventContext::TouchListType); -#endif - - Vector<std::unique_ptr<EventContext>, 32> m_path; -}; - -class EventRelatedNodeResolver { -public: - EventRelatedNodeResolver(Node& relatedNode) - : m_relatedNode(relatedNode) - , m_relatedNodeTreeScope(relatedNode.treeScope()) - , m_relatedNodeInCurrentTreeScope(nullptr) - , m_currentTreeScope(nullptr) -#if ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS) - , m_touch(0) - , m_touchListType(TouchEventContext::NotTouchList) -#endif - { - } - -#if ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS) - EventRelatedNodeResolver(Touch& touch, TouchEventContext::TouchListType touchListType) - : m_relatedNode(*touch.target()->toNode()) - , m_relatedNodeTreeScope(m_relatedNode.treeScope()) - , m_relatedNodeInCurrentTreeScope(nullptr) - , m_currentTreeScope(nullptr) - , m_touch(&touch) - , m_touchListType(touchListType) - { - ASSERT(touch.target()->toNode()); - } -#endif - -#if ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS) - Touch* touch() const { return m_touch; } - TouchEventContext::TouchListType touchListType() const { return m_touchListType; } -#endif - - Node* moveToParentOrShadowHost(Node& newTarget) - { - TreeScope& newTreeScope = newTarget.treeScope(); - if (&newTreeScope == m_currentTreeScope) - return m_relatedNodeInCurrentTreeScope; - - if (m_currentTreeScope) { - ASSERT(m_currentTreeScope->rootNode()->isShadowRoot()); - ASSERT(&newTarget == toShadowRoot(m_currentTreeScope->rootNode())->hostElement()); - ASSERT(m_currentTreeScope->parentTreeScope() == &newTreeScope); - } - - if (m_relatedNodeInCurrentTreeScope) { // relatedNode is under the current tree scope - ASSERT(m_currentTreeScope); - m_relatedNodeInCurrentTreeScope = &newTarget; - } else if (&newTreeScope == &m_relatedNodeTreeScope) // relatedNode is in the current tree scope; - m_relatedNodeInCurrentTreeScope = &m_relatedNode; - // Otherwise, we haven't reached the tree scope that contains relatedNode yet. - - m_currentTreeScope = &newTreeScope; - - return m_relatedNodeInCurrentTreeScope; - } - -private: - Node& m_relatedNode; - const TreeScope& m_relatedNodeTreeScope; - Node* m_relatedNodeInCurrentTreeScope; - TreeScope* m_currentTreeScope; -#if ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS) - Touch* m_touch; - TouchEventContext::TouchListType m_touchListType; -#endif -}; - -inline EventTarget& eventTargetRespectingTargetRules(Node& referenceNode) -{ - if (referenceNode.isPseudoElement()) { - EventTarget* hostElement = toPseudoElement(referenceNode).hostElement(); - ASSERT(hostElement); - return *hostElement; - } - -#if ENABLE(SVG) - if (!referenceNode.isSVGElement() || !referenceNode.isInShadowTree()) - return referenceNode; - - // Spec: The event handling for the non-exposed tree works as if the referenced element had been textually included - // as a deeply cloned child of the 'use' element, except that events are dispatched to the SVGElementInstance objects - Node* rootNode = referenceNode.treeScope().rootNode(); - Element* shadowHostElement = rootNode->isShadowRoot() ? toShadowRoot(rootNode)->hostElement() : 0; - // At this time, SVG nodes are not supported in non-<use> shadow trees. - if (!shadowHostElement || !shadowHostElement->hasTagName(SVGNames::useTag)) - return referenceNode; - SVGUseElement* useElement = toSVGUseElement(shadowHostElement); - if (SVGElementInstance* instance = useElement->instanceForShadowTreeElement(&referenceNode)) - return *instance; -#endif - - return referenceNode; -} - -void EventDispatcher::dispatchScopedEvent(Node& node, PassRefPtr<Event> event) +void EventDispatcher::dispatchScopedEvent(Node& node, Event& event) { // We need to set the target here because it can go away by the time we actually fire the event. - event->setTarget(&eventTargetRespectingTargetRules(node)); - ScopedEventQueue::instance().enqueueEvent(event); + event.setTarget(EventPath::eventTargetRespectingTargetRules(node)); + ScopedEventQueue::singleton().enqueueEvent(event); } -void EventDispatcher::dispatchSimulatedClick(Element* element, Event* underlyingEvent, SimulatedClickMouseEventOptions mouseEventOptions, SimulatedClickVisualOptions visualOptions) +static void callDefaultEventHandlersInTheBubblingOrder(Event& event, const EventPath& path) { - if (element->isDisabledFormControl()) - return; - - DEFINE_STATIC_LOCAL(HashSet<Element*>, elementsDispatchingSimulatedClicks, ()); - if (!elementsDispatchingSimulatedClicks.add(element).isNewEntry) + if (path.isEmpty()) return; - if (mouseEventOptions == SendMouseOverUpDownEvents) - dispatchEvent(element, SimulatedMouseEvent::create(eventNames().mouseoverEvent, element->document().defaultView(), underlyingEvent, element)); - - if (mouseEventOptions != SendNoEvents) - dispatchEvent(element, SimulatedMouseEvent::create(eventNames().mousedownEvent, element->document().defaultView(), underlyingEvent, element)); - element->setActive(true, visualOptions == ShowPressedLook); - if (mouseEventOptions != SendNoEvents) - dispatchEvent(element, SimulatedMouseEvent::create(eventNames().mouseupEvent, element->document().defaultView(), underlyingEvent, element)); - element->setActive(false); - - // always send click - dispatchEvent(element, SimulatedMouseEvent::create(eventNames().clickEvent, element->document().defaultView(), underlyingEvent, element)); - - elementsDispatchingSimulatedClicks.remove(element); -} - -static void callDefaultEventHandlersInTheBubblingOrder(Event& event, const EventPath& path) -{ // Non-bubbling events call only one default event handler, the one for the target. - path.contextAt(0).node()->defaultEventHandler(&event); + path.contextAt(0).node()->defaultEventHandler(event); ASSERT(!event.defaultPrevented()); if (event.defaultHandled() || !event.bubbles()) @@ -244,24 +65,18 @@ static void callDefaultEventHandlersInTheBubblingOrder(Event& event, const Event size_t size = path.size(); for (size_t i = 1; i < size; ++i) { - path.contextAt(i).node()->defaultEventHandler(&event); + path.contextAt(i).node()->defaultEventHandler(event); ASSERT(!event.defaultPrevented()); if (event.defaultHandled()) return; } } -static void dispatchEventInDOM(Event& event, const EventPath& path, WindowEventContext& windowEventContext) +static void dispatchEventInDOM(Event& event, const EventPath& path) { // Trigger capturing event handlers, starting at the top and working our way down. event.setEventPhase(Event::CAPTURING_PHASE); - // We don't dispatch load events to the window. This quirk was originally - // added because Mozilla doesn't propagate load events to the window object. - bool shouldFireEventAtWindow = event.type() != eventNames().loadEvent; - if (shouldFireEventAtWindow && windowEventContext.handleLocalEvents(event) && event.propagationStopped()) - return; - for (size_t i = path.size() - 1; i > 0; --i) { const EventContext& eventContext = path.contextAt(i); if (eventContext.currentTargetSameAsTarget()) @@ -282,7 +97,7 @@ static void dispatchEventInDOM(Event& event, const EventPath& path, WindowEventC const EventContext& eventContext = path.contextAt(i); if (eventContext.currentTargetSameAsTarget()) event.setEventPhase(Event::AT_TARGET); - else if (event.bubbles() && !event.cancelBubble()) + else if (event.bubbles()) event.setEventPhase(Event::BUBBLING_PHASE); else continue; @@ -290,200 +105,83 @@ static void dispatchEventInDOM(Event& event, const EventPath& path, WindowEventC if (event.propagationStopped()) return; } - if (event.bubbles() && !event.cancelBubble()) { - event.setEventPhase(Event::BUBBLING_PHASE); - if (shouldFireEventAtWindow) - windowEventContext.handleLocalEvents(event); - } } -bool EventDispatcher::dispatchEvent(Node* origin, PassRefPtr<Event> prpEvent) +static bool shouldSuppressEventDispatchInDOM(Node& node, Event& event) { - ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden()); - if (!prpEvent) - return true; - - ASSERT(origin); - RefPtr<Node> node(origin); - RefPtr<Event> event(prpEvent); - RefPtr<FrameView> view = node->document().view(); - EventPath eventPath(*node, *event); - - if (EventTarget* relatedTarget = event->relatedTarget()) - eventPath.setRelatedTarget(*relatedTarget); -#if ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS) - if (event->isTouchEvent()) { - if (!eventPath.updateTouchLists(*toTouchEvent(event.get()))) - return true; - } -#endif - - ChildNodesLazySnapshot::takeChildNodesLazySnapshot(); - - event->setTarget(&eventTargetRespectingTargetRules(*node)); - ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden()); - ASSERT(event->target()); - WindowEventContext windowEventContext(node.get(), eventPath.lastContextIfExists()); - - InputElementClickState clickHandlingState; - if (isHTMLInputElement(node.get())) - toHTMLInputElement(*node).willDispatchEvent(*event, clickHandlingState); - - if (!event->propagationStopped() && !eventPath.isEmpty()) - dispatchEventInDOM(*event, eventPath, windowEventContext); - - event->setTarget(&eventTargetRespectingTargetRules(*node)); - event->setCurrentTarget(0); - event->setEventPhase(0); - - if (clickHandlingState.stateful) - toHTMLInputElement(*node).didDispatchClickEvent(*event, clickHandlingState); - - // Call default event handlers. While the DOM does have a concept of preventing - // default handling, the detail of which handlers are called is an internal - // implementation detail and not part of the DOM. - if (!event->defaultPrevented() && !event->defaultHandled()) - callDefaultEventHandlersInTheBubblingOrder(*event, eventPath); + if (!event.isTrusted()) + return false; - // Ensure that after event dispatch, the event's target object is the - // outermost shadow DOM boundary. - event->setTarget(windowEventContext.target()); - event->setCurrentTarget(0); + auto frame = node.document().frame(); + if (!frame) + return false; - return !event->defaultPrevented(); -} + if (!frame->mainFrame().loader().shouldSuppressKeyboardInput()) + return false; -static inline bool shouldEventCrossShadowBoundary(Event& event, ShadowRoot& shadowRoot, EventTarget& target) -{ - Node* targetNode = target.toNode(); -#if ENABLE(FULLSCREEN_API) && ENABLE(VIDEO) - // Video-only full screen is a mode where we use the shadow DOM as an implementation - // detail that should not be detectable by the web content. - if (targetNode) { - if (Element* element = targetNode->document().webkitCurrentFullScreenElement()) { - // FIXME: We assume that if the full screen element is a media element that it's - // the video-only full screen. Both here and elsewhere. But that is probably wrong. - if (element->isMediaElement() && shadowRoot.hostElement() == element) - return false; - } + if (is<TextEvent>(event)) { + auto& textEvent = downcast<TextEvent>(event); + return textEvent.isKeyboard() || textEvent.isComposition(); } -#endif - // WebKit never allowed selectstart event to cross the the shadow DOM boundary. - // Changing this breaks existing sites. - // See https://bugs.webkit.org/show_bug.cgi?id=52195 for details. - const AtomicString& eventType = event.type(); - bool targetIsInShadowRoot = targetNode && targetNode->treeScope().rootNode() == &shadowRoot; - return !targetIsInShadowRoot - || !(eventType == eventNames().abortEvent - || eventType == eventNames().changeEvent - || eventType == eventNames().errorEvent - || eventType == eventNames().loadEvent - || eventType == eventNames().resetEvent - || eventType == eventNames().resizeEvent - || eventType == eventNames().scrollEvent - || eventType == eventNames().selectEvent - || eventType == eventNames().selectstartEvent); + return is<CompositionEvent>(event) || is<InputEvent>(event) || is<KeyboardEvent>(event); } -static Node* nodeOrHostIfPseudoElement(Node* node) +bool EventDispatcher::dispatchEvent(Node& node, Event& event) { - return node->isPseudoElement() ? toPseudoElement(node)->hostElement() : node; -} + ASSERT_WITH_SECURITY_IMPLICATION(NoEventDispatchAssertion::isEventAllowedInMainThread()); + Ref<Node> protectedNode(node); + RefPtr<FrameView> view = node.document().view(); + EventPath eventPath(node, event); -EventPath::EventPath(Node& targetNode, Event& event) -{ - bool inDocument = targetNode.inDocument(); - bool isSVGElement = targetNode.isSVGElement(); - bool isMouseOrFocusEvent = event.isMouseEvent() || event.isFocusEvent(); -#if ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS) - bool isTouchEvent = event.isTouchEvent(); + if (EventTarget* relatedTarget = event.relatedTarget()) + eventPath.setRelatedTarget(node, *relatedTarget); +#if ENABLE(TOUCH_EVENTS) + if (is<TouchEvent>(event)) + eventPath.retargetTouchLists(downcast<TouchEvent>(event)); #endif - EventTarget* target = 0; - Node* node = nodeOrHostIfPseudoElement(&targetNode); - while (node) { - if (!target || !isSVGElement) // FIXME: This code doesn't make sense once we've climbed out of the SVG subtree in a HTML document. - target = &eventTargetRespectingTargetRules(*node); - for (; node; node = node->parentNode()) { - EventTarget& currentTarget = eventTargetRespectingTargetRules(*node); - if (isMouseOrFocusEvent) - m_path.append(std::make_unique<MouseOrFocusEventContext>(node, ¤tTarget, target)); -#if ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS) - else if (isTouchEvent) - m_path.append(std::make_unique<TouchEventContext>(node, ¤tTarget, target)); -#endif - else - m_path.append(std::make_unique<EventContext>(node, ¤tTarget, target)); - if (!inDocument) - return; - if (node->isShadowRoot()) - break; - } - if (!node || !shouldEventCrossShadowBoundary(event, *toShadowRoot(node), *target)) - return; - node = toShadowRoot(node)->hostElement(); - } -} + ChildNodesLazySnapshot::takeChildNodesLazySnapshot(); -#if ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS) -static void addRelatedNodeResolversForTouchList(Vector<EventRelatedNodeResolver, 16>& touchTargetResolvers, TouchList* touchList, TouchEventContext::TouchListType type) -{ - const size_t touchListSize = touchList->length(); - for (size_t i = 0; i < touchListSize; ++i) - touchTargetResolvers.append(EventRelatedNodeResolver(*touchList->item(i), type)); -} + EventTarget* target = EventPath::eventTargetRespectingTargetRules(node); + event.setTarget(target); + if (!event.target()) + return true; -bool EventPath::updateTouchLists(const TouchEvent& touchEvent) -{ - if (!touchEvent.touches() || !touchEvent.targetTouches() || !touchEvent.changedTouches()) - return false; - - Vector<EventRelatedNodeResolver, 16> touchTargetResolvers; - const size_t touchNodeCount = touchEvent.touches()->length() + touchEvent.targetTouches()->length() + touchEvent.changedTouches()->length(); - touchTargetResolvers.reserveInitialCapacity(touchNodeCount); + ASSERT_WITH_SECURITY_IMPLICATION(NoEventDispatchAssertion::isEventAllowedInMainThread()); + + InputElementClickState clickHandlingState; + if (is<HTMLInputElement>(node)) + downcast<HTMLInputElement>(node).willDispatchEvent(event, clickHandlingState); - addRelatedNodeResolversForTouchList(touchTargetResolvers, touchEvent.touches(), TouchEventContext::Touches); - addRelatedNodeResolversForTouchList(touchTargetResolvers, touchEvent.targetTouches(), TouchEventContext::TargetTouches); - addRelatedNodeResolversForTouchList(touchTargetResolvers, touchEvent.changedTouches(), TouchEventContext::ChangedTouches); + if (shouldSuppressEventDispatchInDOM(node, event)) + event.stopPropagation(); - ASSERT(touchTargetResolvers.size() == touchNodeCount); - size_t eventPathSize = m_path.size(); - for (size_t i = 0; i < eventPathSize; ++i) { - TouchEventContext& context = toTouchEventContext(*m_path[i]); - Node& nodeToMoveTo = *context.node(); - for (size_t resolverIndex = 0; resolverIndex < touchNodeCount; ++resolverIndex) { - EventRelatedNodeResolver& currentResolver = touchTargetResolvers[resolverIndex]; - Node* nodeInCurrentTreeScope = currentResolver.moveToParentOrShadowHost(nodeToMoveTo); - ASSERT(currentResolver.touch()); - context.touchList(currentResolver.touchListType())->append(currentResolver.touch()->cloneWithNewTarget(nodeInCurrentTreeScope)); - } + if (!event.propagationStopped() && !eventPath.isEmpty()) { + event.setEventPath(eventPath); + dispatchEventInDOM(event, eventPath); + event.clearEventPath(); } - return true; -} -#endif -void EventPath::setRelatedTarget(EventTarget& relatedTarget) -{ - Node* relatedNode = relatedTarget.toNode(); - if (!relatedNode) - return; + auto* finalTarget = event.target(); + event.setTarget(EventPath::eventTargetRespectingTargetRules(node)); + event.setCurrentTarget(nullptr); + event.resetPropagationFlags(); + event.setEventPhase(Event::NONE); - EventRelatedNodeResolver resolver(*relatedNode); + if (clickHandlingState.stateful) + downcast<HTMLInputElement>(node).didDispatchClickEvent(event, clickHandlingState); - size_t eventPathSize = m_path.size(); - for (size_t i = 0; i < eventPathSize; i++) - toMouseOrFocusEventContext(*m_path[i]).setRelatedTarget(resolver.moveToParentOrShadowHost(*m_path[i]->node())); -} + // Call default event handlers. While the DOM does have a concept of preventing + // default handling, the detail of which handlers are called is an internal + // implementation detail and not part of the DOM. + if (!event.defaultPrevented() && !event.defaultHandled()) + callDefaultEventHandlersInTheBubblingOrder(event, eventPath); -bool EventPath::hasEventListeners(const AtomicString& eventType) const -{ - for (size_t i = 0; i < m_path.size(); i++) { - if (m_path[i]->node()->hasEventListeners(eventType)) - return true; - } + event.setTarget(finalTarget); + event.setCurrentTarget(nullptr); - return false; + return !event.defaultPrevented(); } } diff --git a/Source/WebCore/dom/EventDispatcher.h b/Source/WebCore/dom/EventDispatcher.h index 64040dd3c..d84df443d 100644 --- a/Source/WebCore/dom/EventDispatcher.h +++ b/Source/WebCore/dom/EventDispatcher.h @@ -23,27 +23,21 @@ * Boston, MA 02110-1301, USA. */ -#ifndef EventDispatcher_h -#define EventDispatcher_h +#pragma once #include "SimulatedClickOptions.h" #include <wtf/Forward.h> -#include <wtf/PassRefPtr.h> namespace WebCore { -class Element; class Event; class Node; namespace EventDispatcher { -bool dispatchEvent(Node*, PassRefPtr<Event>); -void dispatchScopedEvent(Node&, PassRefPtr<Event>); -void dispatchSimulatedClick(Element*, Event* underlyingEvent, SimulatedClickMouseEventOptions, SimulatedClickVisualOptions); - -}; +bool dispatchEvent(Node&, Event&); +void dispatchScopedEvent(Node&, Event&); } -#endif +} // namespace WebCore diff --git a/Source/WebCore/dom/EventException.cpp b/Source/WebCore/dom/EventException.cpp deleted file mode 100644 index 487f6b3ad..000000000 --- a/Source/WebCore/dom/EventException.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY GOOGLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "EventException.h" - -namespace WebCore { - -static struct EventExceptionNameDescription { - const char* const name; - const char* const description; -} eventExceptions[] = { - { "UNSPECIFIED_EVENT_TYPE_ERR", "The Event's type was not specified by initializing the event before the method was called." }, - { "DISPATCH_REQUEST_ERR", "The Event object is already being dispatched." } -}; - -bool EventException::initializeDescription(ExceptionCode ec, ExceptionCodeDescription* description) -{ - if (ec < EventExceptionOffset || ec > EventExceptionMax) - return false; - - description->typeName = "DOM Events"; - description->code = ec - EventExceptionOffset; - description->type = EventExceptionType; - - size_t tableSize = WTF_ARRAY_LENGTH(eventExceptions); - size_t tableIndex = ec - UNSPECIFIED_EVENT_TYPE_ERR; - - description->name = tableIndex < tableSize ? eventExceptions[tableIndex].name : 0; - description->description = tableIndex < tableSize ? eventExceptions[tableIndex].description : 0; - - return true; -} - -} // namespace WebCore diff --git a/Source/WebCore/dom/EventException.h b/Source/WebCore/dom/EventException.h deleted file mode 100644 index 71868260c..000000000 --- a/Source/WebCore/dom/EventException.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef EventException_h -#define EventException_h - -#include "ExceptionBase.h" - -namespace WebCore { - -class EventException : public ExceptionBase { -public: - static PassRefPtr<EventException> create(const ExceptionCodeDescription& description) - { - return adoptRef(new EventException(description)); - } - - static const int EventExceptionOffset = 100; - static const int EventExceptionMax = 199; - - enum EventExceptionCode { - UNSPECIFIED_EVENT_TYPE_ERR = EventExceptionOffset, - DISPATCH_REQUEST_ERR - }; - - static bool initializeDescription(ExceptionCode, ExceptionCodeDescription*); - -private: - explicit EventException(const ExceptionCodeDescription& description) - : ExceptionBase(description) - { - } -}; - -} // namespace WebCore - -#endif // EventException_h diff --git a/Source/WebCore/dom/EventException.idl b/Source/WebCore/dom/EventException.idl deleted file mode 100644 index f97c2aadd..000000000 --- a/Source/WebCore/dom/EventException.idl +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2007 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -// Introduced in DOM Level 2: -[ - JSNoStaticTables, - DoNotCheckConstants, - ImplementationLacksVTable, -] exception EventException { - - readonly attribute unsigned short code; - readonly attribute DOMString name; - readonly attribute DOMString message; - -#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT - // Override in a Mozilla compatible format - [NotEnumerable] DOMString toString(); -#endif - - // EventExceptionCode - const unsigned short UNSPECIFIED_EVENT_TYPE_ERR = 0; - const unsigned short DISPATCH_REQUEST_ERR = 1; -}; - diff --git a/Source/WebCore/dom/EventInit.h b/Source/WebCore/dom/EventInit.h new file mode 100644 index 000000000..a548f4e85 --- /dev/null +++ b/Source/WebCore/dom/EventInit.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +namespace WebCore { + +struct EventInit { + bool bubbles { false }; + bool cancelable { false }; + bool composed { false }; +}; + +} diff --git a/Source/WebCore/dom/EventInit.idl b/Source/WebCore/dom/EventInit.idl new file mode 100644 index 000000000..e55a03034 --- /dev/null +++ b/Source/WebCore/dom/EventInit.idl @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +dictionary EventInit { + boolean bubbles = false; + boolean cancelable = false; + boolean composed = false; +}; diff --git a/Source/WebCore/dom/EventListener.h b/Source/WebCore/dom/EventListener.h index 22206098c..27d4cce93 100644 --- a/Source/WebCore/dom/EventListener.h +++ b/Source/WebCore/dom/EventListener.h @@ -18,57 +18,54 @@ * */ -#ifndef EventListener_h -#define EventListener_h +#pragma once #include <wtf/RefCounted.h> namespace JSC { - class JSObject; - class SlotVisitor; +class JSObject; +class SlotVisitor; } namespace WebCore { - class ScriptExecutionContext; - class Event; +class ScriptExecutionContext; +class Event; - class EventListener : public RefCounted<EventListener> { - public: - enum Type { - JSEventListenerType, - ImageEventListenerType, - ObjCEventListenerType, - CPPEventListenerType, - ConditionEventListenerType, - GObjectEventListenerType, - NativeEventListenerType, - SVGTRefTargetEventListenerType, - MediaControlsAppleEventListenerType - }; - - virtual ~EventListener() { } - virtual bool operator==(const EventListener&) = 0; - virtual void handleEvent(ScriptExecutionContext*, Event*) = 0; - virtual bool wasCreatedFromMarkup() const { return false; } +class EventListener : public RefCounted<EventListener> { +public: + enum Type { + JSEventListenerType, + ImageEventListenerType, + ObjCEventListenerType, + CPPEventListenerType, + ConditionEventListenerType, + GObjectEventListenerType, + NativeEventListenerType, + SVGTRefTargetEventListenerType, + MediaControlsAppleEventListenerType + }; - virtual void visitJSFunction(JSC::SlotVisitor&) { } + virtual ~EventListener() { } + virtual bool operator==(const EventListener&) const = 0; + virtual void handleEvent(ScriptExecutionContext*, Event*) = 0; + virtual bool wasCreatedFromMarkup() const { return false; } - bool isAttribute() const { return virtualisAttribute(); } - Type type() const { return m_type; } + virtual void visitJSFunction(JSC::SlotVisitor&) { } - protected: - explicit EventListener(Type type) - : m_type(type) - { - } + bool isAttribute() const { return virtualisAttribute(); } + Type type() const { return m_type; } - private: - virtual bool virtualisAttribute() const { return false; } - - Type m_type; - }; +protected: + explicit EventListener(Type type) + : m_type(type) + { + } -} +private: + virtual bool virtualisAttribute() const { return false; } + + Type m_type; +}; -#endif +} // namespace WebCore diff --git a/Source/WebCore/dom/EventListener.idl b/Source/WebCore/dom/EventListener.idl index c565021ef..72d1f3fd4 100644 --- a/Source/WebCore/dom/EventListener.idl +++ b/Source/WebCore/dom/EventListener.idl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006 Apple Inc. * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> * * This library is free software; you can redistribute it and/or @@ -18,13 +18,8 @@ * Boston, MA 02110-1301, USA. */ -// Introduced in DOM Level 2: [ - NoInterfaceObject, - JSNoStaticTables, - ObjCProtocol, - CPPPureInterface, + NoInterfaceObject ] interface EventListener { - void handleEvent(Event evt); + void handleEvent(Event event); }; - diff --git a/Source/WebCore/dom/EventListenerMap.cpp b/Source/WebCore/dom/EventListenerMap.cpp index a7df1aa22..3141a2e21 100644 --- a/Source/WebCore/dom/EventListenerMap.cpp +++ b/Source/WebCore/dom/EventListenerMap.cpp @@ -16,10 +16,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -34,66 +34,56 @@ #include "EventListenerMap.h" #include "Event.h" -#include "EventException.h" #include "EventTarget.h" #include <wtf/MainThread.h> #include <wtf/StdLibExtras.h> #include <wtf/Vector.h> -#ifndef NDEBUG -#include <wtf/Threading.h> -#endif - using namespace WTF; namespace WebCore { #ifndef NDEBUG -static Mutex& activeIteratorCountMutex() +void EventListenerMap::assertNoActiveIterators() const { - DEFINE_STATIC_LOCAL(Mutex, mutex, ()); - return mutex; -} - -void EventListenerMap::assertNoActiveIterators() -{ - MutexLocker locker(activeIteratorCountMutex()); ASSERT(!m_activeIteratorCount); } #endif EventListenerMap::EventListenerMap() -#ifndef NDEBUG - : m_activeIteratorCount(0) -#endif { } -bool EventListenerMap::contains(const AtomicString& eventType) const +bool EventListenerMap::containsCapturing(const AtomicString& eventType) const { - for (unsigned i = 0; i < m_entries.size(); ++i) { - if (m_entries[i].first == eventType) + auto* listeners = find(eventType); + if (!listeners) + return false; + + for (auto& eventListener : *listeners) { + if (eventListener->useCapture()) return true; } return false; } -bool EventListenerMap::containsCapturing(const AtomicString& eventType) const +bool EventListenerMap::containsActive(const AtomicString& eventType) const { - for (unsigned i = 0; i < m_entries.size(); ++i) { - if (m_entries[i].first == eventType) { - const EventListenerVector* vector = m_entries[i].second.get(); - for (unsigned j = 0; j < vector->size(); ++j) { - if (vector->at(j).useCapture) - return true; - } - } + auto* listeners = find(eventType); + if (!listeners) + return false; + + for (auto& eventListener : *listeners) { + if (!eventListener->isPassive()) + return true; } return false; } void EventListenerMap::clear() { + auto locker = holdLock(m_lock); + assertNoActiveIterators(); m_entries.clear(); @@ -104,53 +94,76 @@ Vector<AtomicString> EventListenerMap::eventTypes() const Vector<AtomicString> types; types.reserveInitialCapacity(m_entries.size()); - for (unsigned i = 0; i < m_entries.size(); ++i) - types.uncheckedAppend(m_entries[i].first); + for (auto& entry : m_entries) + types.uncheckedAppend(entry.first); return types; } -static bool addListenerToVector(EventListenerVector* vector, PassRefPtr<EventListener> listener, bool useCapture) +static inline size_t findListener(const EventListenerVector& listeners, EventListener& listener, bool useCapture) { - RegisteredEventListener registeredListener(listener, useCapture); + for (size_t i = 0; i < listeners.size(); ++i) { + auto& registeredListener = listeners[i]; + if (registeredListener->callback() == listener && registeredListener->useCapture() == useCapture) + return i; + } + return notFound; +} - if (vector->find(registeredListener) != notFound) - return false; // Duplicate listener. +void EventListenerMap::replace(const AtomicString& eventType, EventListener& oldListener, Ref<EventListener>&& newListener, const RegisteredEventListener::Options& options) +{ + auto locker = holdLock(m_lock); + + assertNoActiveIterators(); - vector->append(registeredListener); - return true; + auto* listeners = find(eventType); + ASSERT(listeners); + size_t index = findListener(*listeners, oldListener, options.capture); + ASSERT(index != notFound); + auto& registeredListener = listeners->at(index); + registeredListener->markAsRemoved(); + registeredListener = RegisteredEventListener::create(WTFMove(newListener), options); } -bool EventListenerMap::add(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture) +bool EventListenerMap::add(const AtomicString& eventType, Ref<EventListener>&& listener, const RegisteredEventListener::Options& options) { + auto locker = holdLock(m_lock); + assertNoActiveIterators(); - for (unsigned i = 0; i < m_entries.size(); ++i) { - if (m_entries[i].first == eventType) - return addListenerToVector(m_entries[i].second.get(), listener, useCapture); + if (auto* listeners = find(eventType)) { + if (findListener(*listeners, listener, options.capture) != notFound) + return false; // Duplicate listener. + listeners->append(RegisteredEventListener::create(WTFMove(listener), options)); + return true; } - m_entries.append(std::make_pair(eventType, adoptPtr(new EventListenerVector))); - return addListenerToVector(m_entries.last().second.get(), listener, useCapture); + auto listeners = std::make_unique<EventListenerVector>(); + listeners->uncheckedAppend(RegisteredEventListener::create(WTFMove(listener), options)); + m_entries.append({ eventType, WTFMove(listeners) }); + return true; } -static bool removeListenerFromVector(EventListenerVector* listenerVector, EventListener* listener, bool useCapture, size_t& indexOfRemovedListener) +static bool removeListenerFromVector(EventListenerVector& listeners, EventListener& listener, bool useCapture) { - RegisteredEventListener registeredListener(listener, useCapture); - indexOfRemovedListener = listenerVector->find(registeredListener); - if (indexOfRemovedListener == notFound) + size_t indexOfRemovedListener = findListener(listeners, listener, useCapture); + if (UNLIKELY(indexOfRemovedListener == notFound)) return false; - listenerVector->remove(indexOfRemovedListener); + + listeners[indexOfRemovedListener]->markAsRemoved(); + listeners.remove(indexOfRemovedListener); return true; } -bool EventListenerMap::remove(const AtomicString& eventType, EventListener* listener, bool useCapture, size_t& indexOfRemovedListener) +bool EventListenerMap::remove(const AtomicString& eventType, EventListener& listener, bool useCapture) { + auto locker = holdLock(m_lock); + assertNoActiveIterators(); for (unsigned i = 0; i < m_entries.size(); ++i) { if (m_entries[i].first == eventType) { - bool wasRemoved = removeListenerFromVector(m_entries[i].second.get(), listener, useCapture, indexOfRemovedListener); + bool wasRemoved = removeListenerFromVector(*m_entries[i].second, listener, useCapture); if (m_entries[i].second->isEmpty()) m_entries.remove(i); return wasRemoved; @@ -160,42 +173,37 @@ bool EventListenerMap::remove(const AtomicString& eventType, EventListener* list return false; } -EventListenerVector* EventListenerMap::find(const AtomicString& eventType) +EventListenerVector* EventListenerMap::find(const AtomicString& eventType) const { - assertNoActiveIterators(); - - for (unsigned i = 0; i < m_entries.size(); ++i) { - if (m_entries[i].first == eventType) - return m_entries[i].second.get(); + for (auto& entry : m_entries) { + if (entry.first == eventType) + return entry.second.get(); } - return 0; + return nullptr; } -#if ENABLE(SVG) - -static void removeFirstListenerCreatedFromMarkup(EventListenerVector* listenerVector) +static void removeFirstListenerCreatedFromMarkup(EventListenerVector& listenerVector) { - bool foundListener = false; - - for (size_t i = 0; i < listenerVector->size(); ++i) { - if (!listenerVector->at(i).listener->wasCreatedFromMarkup()) - continue; - foundListener = true; - listenerVector->remove(i); - break; - } - + bool foundListener = listenerVector.removeFirstMatching([] (const auto& registeredListener) { + if (registeredListener->callback().wasCreatedFromMarkup()) { + registeredListener->markAsRemoved(); + return true; + } + return false; + }); ASSERT_UNUSED(foundListener, foundListener); } void EventListenerMap::removeFirstEventListenerCreatedFromMarkup(const AtomicString& eventType) { + auto locker = holdLock(m_lock); + assertNoActiveIterators(); for (unsigned i = 0; i < m_entries.size(); ++i) { if (m_entries[i].first == eventType) { - removeFirstListenerCreatedFromMarkup(m_entries[i].second.get()); + removeFirstListenerCreatedFromMarkup(*m_entries[i].second); if (m_entries[i].second->isEmpty()) m_entries.remove(i); return; @@ -203,37 +211,23 @@ void EventListenerMap::removeFirstEventListenerCreatedFromMarkup(const AtomicStr } } -static void copyListenersNotCreatedFromMarkupToTarget(const AtomicString& eventType, EventListenerVector* listenerVector, EventTarget* target) +static void copyListenersNotCreatedFromMarkupToTarget(const AtomicString& eventType, EventListenerVector& listenerVector, EventTarget* target) { - for (size_t i = 0; i < listenerVector->size(); ++i) { + for (auto& registeredListener : listenerVector) { // Event listeners created from markup have already been transfered to the shadow tree during cloning. - if ((*listenerVector)[i].listener->wasCreatedFromMarkup()) + if (registeredListener->callback().wasCreatedFromMarkup()) continue; - target->addEventListener(eventType, (*listenerVector)[i].listener, (*listenerVector)[i].useCapture); + target->addEventListener(eventType, registeredListener->callback(), registeredListener->useCapture()); } } void EventListenerMap::copyEventListenersNotCreatedFromMarkupToTarget(EventTarget* target) { - assertNoActiveIterators(); - - for (unsigned i = 0; i < m_entries.size(); ++i) - copyListenersNotCreatedFromMarkupToTarget(m_entries[i].first, m_entries[i].second.get(), target); -} - -#endif // ENABLE(SVG) - -EventListenerIterator::EventListenerIterator() - : m_map(0) - , m_entryIndex(0) - , m_index(0) -{ + for (auto& entry : m_entries) + copyListenersNotCreatedFromMarkupToTarget(entry.first, *entry.second, target); } EventListenerIterator::EventListenerIterator(EventTarget* target) - : m_map(0) - , m_entryIndex(0) - , m_index(0) { ASSERT(target); EventTargetData* data = target->eventTargetData(); @@ -244,36 +238,40 @@ EventListenerIterator::EventListenerIterator(EventTarget* target) m_map = &data->eventListenerMap; #ifndef NDEBUG - { - MutexLocker locker(activeIteratorCountMutex()); - m_map->m_activeIteratorCount++; - } + m_map->m_activeIteratorCount++; +#endif +} + +EventListenerIterator::EventListenerIterator(EventListenerMap* map) +{ + m_map = map; + +#ifndef NDEBUG + m_map->m_activeIteratorCount++; #endif } #ifndef NDEBUG EventListenerIterator::~EventListenerIterator() { - if (m_map) { - MutexLocker locker(activeIteratorCountMutex()); + if (m_map) m_map->m_activeIteratorCount--; - } } #endif EventListener* EventListenerIterator::nextListener() { if (!m_map) - return 0; + return nullptr; for (; m_entryIndex < m_map->m_entries.size(); ++m_entryIndex) { EventListenerVector& listeners = *m_map->m_entries[m_entryIndex].second; if (m_index < listeners.size()) - return listeners[m_index++].listener.get(); + return &listeners[m_index++]->callback(); m_index = 0; } - return 0; + return nullptr; } } // namespace WebCore diff --git a/Source/WebCore/dom/EventListenerMap.h b/Source/WebCore/dom/EventListenerMap.h index 03a1db4ef..ae5f98d23 100644 --- a/Source/WebCore/dom/EventListenerMap.h +++ b/Source/WebCore/dom/EventListenerMap.h @@ -16,10 +16,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -30,56 +30,62 @@ * */ -#ifndef EventListenerMap_h -#define EventListenerMap_h +#pragma once #include "RegisteredEventListener.h" +#include <atomic> +#include <memory> #include <wtf/Forward.h> -#include <wtf/PassOwnPtr.h> -#include <wtf/text/AtomicStringHash.h> +#include <wtf/Lock.h> +#include <wtf/text/AtomicString.h> namespace WebCore { class EventTarget; -typedef Vector<RegisteredEventListener, 1> EventListenerVector; +using EventListenerVector = Vector<RefPtr<RegisteredEventListener>, 1>; class EventListenerMap { public: EventListenerMap(); bool isEmpty() const { return m_entries.isEmpty(); } - bool contains(const AtomicString& eventType) const; + bool contains(const AtomicString& eventType) const { return find(eventType); } bool containsCapturing(const AtomicString& eventType) const; + bool containsActive(const AtomicString& eventType) const; void clear(); - bool add(const AtomicString& eventType, PassRefPtr<EventListener>, bool useCapture); - bool remove(const AtomicString& eventType, EventListener*, bool useCapture, size_t& indexOfRemovedListener); - EventListenerVector* find(const AtomicString& eventType); + + void replace(const AtomicString& eventType, EventListener& oldListener, Ref<EventListener>&& newListener, const RegisteredEventListener::Options&); + bool add(const AtomicString& eventType, Ref<EventListener>&&, const RegisteredEventListener::Options&); + bool remove(const AtomicString& eventType, EventListener&, bool useCapture); + WEBCORE_EXPORT EventListenerVector* find(const AtomicString& eventType) const; Vector<AtomicString> eventTypes() const; -#if ENABLE(SVG) void removeFirstEventListenerCreatedFromMarkup(const AtomicString& eventType); void copyEventListenersNotCreatedFromMarkupToTarget(EventTarget*); -#endif + + Lock& lock() { return m_lock; } private: friend class EventListenerIterator; - void assertNoActiveIterators(); + void assertNoActiveIterators() const; - Vector<std::pair<AtomicString, OwnPtr<EventListenerVector>>, 2> m_entries; + Vector<std::pair<AtomicString, std::unique_ptr<EventListenerVector>>, 2> m_entries; #ifndef NDEBUG - int m_activeIteratorCount; + std::atomic<int> m_activeIteratorCount { 0 }; #endif + + Lock m_lock; }; class EventListenerIterator { WTF_MAKE_NONCOPYABLE(EventListenerIterator); public: - EventListenerIterator(); explicit EventListenerIterator(EventTarget*); + explicit EventListenerIterator(EventListenerMap*); #ifndef NDEBUG ~EventListenerIterator(); #endif @@ -87,15 +93,13 @@ public: EventListener* nextListener(); private: - EventListenerMap* m_map; - unsigned m_entryIndex; - unsigned m_index; + EventListenerMap* m_map { nullptr }; + unsigned m_entryIndex { 0 }; + unsigned m_index { 0 }; }; #ifdef NDEBUG -inline void EventListenerMap::assertNoActiveIterators() { } +inline void EventListenerMap::assertNoActiveIterators() const { } #endif } // namespace WebCore - -#endif // EventListenerMap_h diff --git a/Source/WebCore/dom/EventModifierInit.h b/Source/WebCore/dom/EventModifierInit.h new file mode 100644 index 000000000..82fe54e17 --- /dev/null +++ b/Source/WebCore/dom/EventModifierInit.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "UIEventInit.h" + +namespace WebCore { + +struct EventModifierInit : UIEventInit { + bool ctrlKey { false }; + bool shiftKey { false }; + bool altKey { false }; + bool metaKey { false }; + + bool modifierAltGraph { false }; + bool modifierCapsLock { false }; +}; + +} diff --git a/Source/WebCore/dom/EventModifierInit.idl b/Source/WebCore/dom/EventModifierInit.idl new file mode 100644 index 000000000..e97d745b2 --- /dev/null +++ b/Source/WebCore/dom/EventModifierInit.idl @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2016 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +dictionary EventModifierInit : UIEventInit { + boolean ctrlKey = false; + boolean shiftKey = false; + boolean altKey = false; + boolean metaKey = false; + + boolean modifierAltGraph = false; + boolean modifierCapsLock = false; + + // FIXME: The following members are in the specification but are not implemented. + // boolean modifierFn = false; + // boolean modifierFnLock = false; + // boolean modifierHyper = false; + // boolean modifierNumLock = false; + // boolean modifierScrollLock = false; + // boolean modifierSuper = false; + // boolean modifierSymbol = false; + // boolean modifierSymbolLock = false; +}; diff --git a/Source/WebCore/dom/EventNames.cpp b/Source/WebCore/dom/EventNames.cpp index 9b9f49b94..fcc5601fc 100644 --- a/Source/WebCore/dom/EventNames.cpp +++ b/Source/WebCore/dom/EventNames.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005 Apple Computer, Inc. + * Copyright (C) 2005, 2015 Apple Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -24,11 +24,10 @@ namespace WebCore { #define INITIALIZE_EVENT_NAME(name) \ - , name##Event(#name, AtomicString::ConstructFromLiteral) + name##Event(#name, AtomicString::ConstructFromLiteral), EventNames::EventNames() - : dummy(0) - DOM_EVENT_NAMES_FOR_EACH(INITIALIZE_EVENT_NAME) + : DOM_EVENT_NAMES_FOR_EACH(INITIALIZE_EVENT_NAME) dummy(0) { } diff --git a/Source/WebCore/dom/EventNames.h b/Source/WebCore/dom/EventNames.h index 294f239d5..877ffb704 100644 --- a/Source/WebCore/dom/EventNames.h +++ b/Source/WebCore/dom/EventNames.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2005, 2007, 2015 Apple Inc. All rights reserved. * Copyright (C) 2006 Jon Shier (jshier@iastate.edu) * * This library is free software; you can redistribute it and/or @@ -19,25 +19,59 @@ * */ -#ifndef EventNames_h -#define EventNames_h +#pragma once #include "ThreadGlobalData.h" +#include <array> +#include <functional> #include <wtf/text/AtomicString.h> namespace WebCore { +#if !defined(ADDITIONAL_DOM_EVENT_NAMES_FOR_EACH) +#define ADDITIONAL_DOM_EVENT_NAMES_FOR_EACH(macro) +#endif + #define DOM_EVENT_NAMES_FOR_EACH(macro) \ - \ + ADDITIONAL_DOM_EVENT_NAMES_FOR_EACH(macro) \ + macro(DOMActivate) \ + macro(DOMCharacterDataModified) \ + macro(DOMContentLoaded) \ + macro(DOMFocusIn) \ + macro(DOMFocusOut) \ + macro(DOMNodeInserted) \ + macro(DOMNodeInsertedIntoDocument) \ + macro(DOMNodeRemoved) \ + macro(DOMNodeRemovedFromDocument) \ + macro(DOMSubtreeModified) \ macro(abort) \ + macro(active) \ + macro(addsourcebuffer) \ + macro(addstream) \ + macro(addtrack) \ + macro(animationend) \ + macro(animationiteration) \ + macro(animationstart) \ + macro(audioend) \ + macro(audioprocess) \ + macro(audiostart) \ + macro(autocomplete) \ + macro(autocompleteerror) \ macro(beforecopy) \ macro(beforecut) \ + macro(beforeinput) \ macro(beforeload) \ macro(beforepaste) \ macro(beforeunload) \ + macro(beginEvent) \ macro(blocked) \ macro(blur) \ + macro(boundary) \ + macro(bufferedAmountLowThreshold) \ macro(cached) \ + macro(cancel) \ + macro(canplay) \ + macro(canplaythrough) \ macro(change) \ macro(chargingchange) \ macro(chargingtimechange) \ @@ -49,14 +83,16 @@ namespace WebCore { macro(compositionstart) \ macro(compositionupdate) \ macro(connect) \ + macro(connecting) \ macro(contextmenu) \ macro(copy) \ + macro(cuechange) \ macro(cut) \ + macro(datachannel) \ macro(dblclick) \ macro(devicemotion) \ macro(deviceorientation) \ macro(dischargingtimechange) \ - macro(display) \ macro(downloading) \ macro(drag) \ macro(dragend) \ @@ -65,26 +101,50 @@ namespace WebCore { macro(dragover) \ macro(dragstart) \ macro(drop) \ + macro(durationchange) \ + macro(emptied) \ + macro(encrypted) \ + macro(end) \ + macro(endEvent) \ + macro(ended) \ + macro(enter) \ macro(error) \ + macro(exit) \ macro(focus) \ macro(focusin) \ macro(focusout) \ - macro(gesturetap) \ - macro(gesturetapdown) \ - macro(gesturescrollstart) \ + macro(gamepadconnected) \ + macro(gamepaddisconnected) \ + macro(gesturechange) \ + macro(gestureend) \ macro(gesturescrollend) \ + macro(gesturescrollstart) \ macro(gesturescrollupdate) \ + macro(gesturestart) \ + macro(gesturetap) \ + macro(gesturetapdown) \ macro(hashchange) \ + macro(icecandidate) \ + macro(iceconnectionstatechange) \ + macro(icegatheringstatechange) \ + macro(inactive) \ macro(input) \ macro(invalid) \ macro(keydown) \ macro(keypress) \ + macro(keystatuseschange) \ macro(keyup) \ + macro(languagechange) \ macro(levelchange) \ macro(load) \ + macro(loadeddata) \ + macro(loadedmetadata) \ + macro(loadend) \ macro(loading) \ macro(loadingdone) \ + macro(loadingerror) \ macro(loadstart) \ + macro(mark) \ macro(message) \ macro(mousedown) \ macro(mouseenter) \ @@ -94,232 +154,217 @@ namespace WebCore { macro(mouseover) \ macro(mouseup) \ macro(mousewheel) \ + macro(mute) \ + macro(negotiationneeded) \ + macro(nexttrack) \ + macro(nomatch) \ macro(noupdate) \ macro(obsolete) \ macro(offline) \ macro(online) \ macro(open) \ + macro(orientationchange) \ + macro(overconstrained) \ macro(overflowchanged) \ macro(pagehide) \ macro(pageshow) \ macro(paste) \ + macro(pause) \ + macro(paymentauthorized) \ + macro(paymentmethodselected) \ + macro(play) \ + macro(playing) \ + macro(pointerlockchange) \ + macro(pointerlockerror) \ macro(popstate) \ + macro(previoustrack) \ + macro(progress) \ + macro(ratechange) \ macro(readystatechange) \ + macro(removesourcebuffer) \ + macro(removestream) \ + macro(removetrack) \ macro(reset) \ macro(resize) \ + macro(resourcetimingbufferfull) \ + macro(result) \ + macro(resume) \ macro(scroll) \ macro(search) \ + macro(securitypolicyviolation) \ + macro(seeked) \ + macro(seeking) \ macro(select) \ - macro(selectstart) \ macro(selectionchange) \ + macro(selectstart) \ + macro(shippingmethodselected) \ + macro(shippingcontactselected) \ + macro(show) \ + macro(signalingstatechange) \ + macro(slotchange) \ + macro(soundend) \ + macro(soundstart) \ + macro(sourceclose) \ + macro(sourceended) \ + macro(sourceopen) \ + macro(speechend) \ + macro(speechstart) \ + macro(stalled) \ + macro(start) \ + macro(started) \ + macro(statechange) \ macro(storage) \ macro(submit) \ + macro(success) \ + macro(suspend) \ macro(textInput) \ + macro(timeout) \ + macro(timeupdate) \ + macro(toggle) \ + macro(tonechange) \ + macro(touchcancel) \ + macro(touchend) \ + macro(touchforcechange) \ + macro(touchmove) \ + macro(touchstart) \ + macro(track) \ + macro(transitionend) \ macro(unload) \ + macro(unmute) \ + macro(update) \ + macro(updateend) \ macro(updateready) \ + macro(updatestart) \ macro(upgradeneeded) \ + macro(validatemerchant) \ macro(versionchange) \ macro(visibilitychange) \ - macro(wheel) \ - macro(write) \ - macro(writeend) \ - macro(writestart) \ - macro(zoom) \ - \ - macro(DOMActivate) \ - macro(DOMFocusIn) \ - macro(DOMFocusOut) \ - macro(DOMCharacterDataModified) \ - macro(DOMNodeInserted) \ - macro(DOMNodeInsertedIntoDocument) \ - macro(DOMNodeRemoved) \ - macro(DOMNodeRemovedFromDocument) \ - macro(DOMSubtreeModified) \ - macro(DOMContentLoaded) \ - \ - macro(webkitBeforeTextInserted) \ - macro(webkitEditableContentChanged) \ - \ - macro(canplay) \ - macro(canplaythrough) \ - macro(durationchange) \ - macro(emptied) \ - macro(ended) \ - macro(loadeddata) \ - macro(loadedmetadata) \ - macro(pause) \ - macro(play) \ - macro(playing) \ - macro(ratechange) \ - macro(seeked) \ - macro(seeking) \ - macro(timeupdate) \ macro(volumechange) \ macro(waiting) \ - \ - macro(addtrack) \ - macro(cuechange) \ - macro(enter) \ - macro(exit) \ - \ + macro(waitingforkey) \ + macro(webglcontextcreationerror) \ + macro(webglcontextlost) \ + macro(webglcontextrestored) \ + macro(webkitAnimationEnd) \ + macro(webkitAnimationIteration) \ + macro(webkitAnimationStart) \ + macro(webkitBeforeTextInserted) \ + macro(webkitEditableContentChanged) \ + macro(webkitTransitionEnd) \ macro(webkitbeginfullscreen) \ + macro(webkitcurrentplaybacktargetiswirelesschanged) \ + macro(webkitdeviceproximity) \ macro(webkitendfullscreen) \ - \ - macro(addsourcebuffer) \ - macro(removesourcebuffer) \ - macro(sourceopen) \ - macro(sourceended) \ - macro(sourceclose) \ - macro(update) \ - macro(updateend) \ - macro(updatestart) \ - macro(webkitaddsourcebuffer) \ - macro(webkitremovesourcebuffer) \ - macro(webkitsourceopen) \ - macro(webkitsourceended) \ - macro(webkitsourceclose) \ - \ + macro(webkitfullscreenchange) \ + macro(webkitfullscreenerror) \ macro(webkitkeyadded) \ macro(webkitkeyerror) \ macro(webkitkeymessage) \ + macro(webkitmouseforcechanged) \ + macro(webkitmouseforcedown) \ + macro(webkitmouseforcewillbegin) \ + macro(webkitmouseforceup) \ macro(webkitneedkey) \ - \ - macro(progress) \ - macro(stalled) \ - macro(suspend) \ - \ - macro(webkitAnimationEnd) \ - macro(webkitAnimationStart) \ - macro(webkitAnimationIteration) \ - \ - macro(webkitTransitionEnd) \ - macro(transitionend) \ - \ - macro(orientationchange) \ - \ - macro(timeout) \ - \ - macro(touchstart) \ - macro(touchmove) \ - macro(touchend) \ - macro(touchcancel) \ - \ - macro(success) \ - \ - macro(loadend) \ - \ - macro(webkitfullscreenchange) \ - macro(webkitfullscreenerror) \ - \ - macro(webkitspeechchange) \ - \ - macro(audiostart) \ - macro(soundstart) \ - macro(speechstart) \ - macro(speechend) \ - macro(soundend) \ - macro(audioend) \ - macro(result) \ - macro(nomatch) \ - macro(start) \ - macro(end) \ - macro(mark) \ - macro(boundary) \ - macro(resume) \ - \ - macro(webglcontextlost) \ - macro(webglcontextrestored) \ - macro(webglcontextcreationerror) \ - \ - macro(audioprocess) \ - \ - macro(connecting) \ - macro(addstream) \ - macro(removestream) \ - macro(signalingstatechange) \ - macro(removetrack) \ - macro(overconstrained) \ - macro(mute) \ - macro(unmute) \ - macro(started) \ - macro(iceconnectionstatechange) \ - macro(icecandidate) \ - macro(negotiationneeded) \ - macro(datachannel) \ - macro(tonechange) \ - \ - macro(show) \ - \ - macro(webkitpointerlockchange) \ - macro(webkitpointerlockerror) \ - \ - macro(webkitregionlayoutupdate) \ - \ - macro(webkitregionoversetchange) \ - \ macro(webkitnetworkinfochange) \ - \ - macro(webkitresourcetimingbufferfull) \ - \ - macro(webkitdeviceproximity) \ - \ - macro(securitypolicyviolation) \ - \ - /* ENABLE(IOS_AIRPLAY) */ \ - macro(webkitcurrentplaybacktargetiswirelesschanged) \ macro(webkitplaybacktargetavailabilitychanged) \ - /* End of ENABLE(IOS_AIRPLAY) */ \ - \ - /* ENABLE(IOS_GESTURE_EVENTS) */ \ - macro(gesturestart) \ - macro(gesturechange) \ - macro(gestureend) \ - /* End of ENABLE(IOS_GESTURE_EVENTS) */ \ - + macro(webkitpresentationmodechanged) \ + macro(webkitregionoversetchange) \ + macro(webkitremovesourcebuffer) \ + macro(webkitsourceclose) \ + macro(webkitsourceended) \ + macro(webkitsourceopen) \ + macro(webkitspeechchange) \ + macro(webkitwillrevealbottom) \ + macro(webkitwillrevealleft) \ + macro(webkitwillrevealright) \ + macro(webkitwillrevealtop) \ + macro(wheel) \ + macro(write) \ + macro(writeend) \ + macro(writestart) \ + macro(zoom) \ // end of DOM_EVENT_NAMES_FOR_EACH -class EventNames { +struct EventNames { WTF_MAKE_NONCOPYABLE(EventNames); WTF_MAKE_FAST_ALLOCATED; - int dummy; // Needed to make initialization macro work. - // Private to prevent accidental call to EventNames() instead of eventNames() - EventNames(); - friend class ThreadGlobalData; public: - #define DOM_EVENT_NAMES_DECLARE(name) AtomicString name##Event; +#define DOM_EVENT_NAMES_DECLARE(name) const AtomicString name##Event; DOM_EVENT_NAMES_FOR_EACH(DOM_EVENT_NAMES_DECLARE) - #undef DOM_EVENT_NAMES_DECLARE +#undef DOM_EVENT_NAMES_DECLARE - inline bool isGestureEventType(const AtomicString& eventType) const + // FIXME: The friend declaration to std::make_unique below does not work in windows port. + // + // template<class T, class... Args> + // friend typename std::_Unique_if<T>::_Single_object std::make_unique(Args&&...); + // + // This create function should be deleted later and is only for keeping EventNames as private. + // std::make_unique should be used instead. + // + template<class... Args> + static std::unique_ptr<EventNames> create(Args&&... args) { - return eventType == gesturestartEvent || eventType == gesturechangeEvent || eventType == gestureendEvent; + return std::unique_ptr<EventNames>(new EventNames(std::forward<Args>(args)...)); } - inline bool isTouchEventType(const AtomicString& eventType) const - { - return eventType == touchstartEvent - || eventType == touchmoveEvent - || eventType == touchendEvent - || eventType == touchcancelEvent; - } + // FIXME: Inelegant to call these both event names and event types. + // We should choose one term and stick to it. + bool isWheelEventType(const AtomicString& eventType) const; + bool isGestureEventType(const AtomicString& eventType) const; + bool isTouchEventType(const AtomicString& eventType) const; +#if ENABLE(GAMEPAD) + bool isGamepadEventType(const AtomicString& eventType) const; +#endif - Vector<AtomicString> touchEventNames() const - { - Vector<AtomicString> names; - names.reserveCapacity(4); - names.append(touchstartEvent); - names.append(touchmoveEvent); - names.append(touchendEvent); - names.append(touchcancelEvent); - return names; - } + std::array<std::reference_wrapper<const AtomicString>, 5> touchEventNames() const; + +private: + EventNames(); // Private to prevent accidental call to EventNames() instead of eventNames(). + friend class ThreadGlobalData; // Allow ThreadGlobalData to create the per-thread EventNames object. + + int dummy; // Needed to make initialization macro work. }; -inline EventNames& eventNames() +const EventNames& eventNames(); + +inline const EventNames& eventNames() { return threadGlobalData().eventNames(); } +inline bool EventNames::isGestureEventType(const AtomicString& eventType) const +{ + return eventType == gesturestartEvent || eventType == gesturechangeEvent || eventType == gestureendEvent; +} + +inline bool EventNames::isTouchEventType(const AtomicString& eventType) const +{ + return eventType == touchstartEvent + || eventType == touchmoveEvent + || eventType == touchendEvent + || eventType == touchcancelEvent + || eventType == touchforcechangeEvent; +} + +inline bool EventNames::isWheelEventType(const AtomicString& eventType) const +{ + return eventType == wheelEvent + || eventType == mousewheelEvent; +} + +inline std::array<std::reference_wrapper<const AtomicString>, 5> EventNames::touchEventNames() const +{ + return { { touchstartEvent, touchmoveEvent, touchendEvent, touchcancelEvent, touchforcechangeEvent } }; +} + +#if ENABLE(GAMEPAD) + +inline bool EventNames::isGamepadEventType(const AtomicString& eventType) const +{ + return eventType == gamepadconnectedEvent + || eventType == gamepaddisconnectedEvent; } #endif + +} // namespace WebCore diff --git a/Source/WebCore/dom/EventNames.in b/Source/WebCore/dom/EventNames.in index d0f313d4c..05ea9e3fa 100644 --- a/Source/WebCore/dom/EventNames.in +++ b/Source/WebCore/dom/EventNames.in @@ -3,14 +3,18 @@ namespace="Event" Event Events interfaceName=Event HTMLEvents interfaceName=Event +AnimationEvent BeforeLoadEvent BeforeUnloadEvent +ClipboardEvent CloseEvent CompositionEvent CustomEvent ErrorEvent FocusEvent HashChangeEvent +InputEvent +InputEvents interfaceName=InputEvent KeyboardEvent KeyboardEvents interfaceName=KeyboardEvent MessageEvent @@ -30,35 +34,40 @@ WebKitAnimationEvent WebKitTransitionEvent WheelEvent XMLHttpRequestProgressEvent +ApplePayPaymentAuthorizedEvent conditional=APPLE_PAY +ApplePayPaymentMethodSelectedEvent conditional=APPLE_PAY +ApplePayShippingContactSelectedEvent conditional=APPLE_PAY +ApplePayShippingMethodSelectedEvent conditional=APPLE_PAY +ApplePayValidateMerchantEvent conditional=APPLE_PAY AudioProcessingEvent conditional=WEB_AUDIO OfflineAudioCompletionEvent conditional=WEB_AUDIO -MediaStreamEvent conditional=MEDIA_STREAM +MediaStreamEvent conditional=WEB_RTC MediaStreamTrackEvent conditional=MEDIA_STREAM -RTCIceCandidateEvent conditional=MEDIA_STREAM -RTCDataChannelEvent conditional=MEDIA_STREAM -RTCDTMFToneChangeEvent conditional=MEDIA_STREAM -SpeechInputEvent conditional=INPUT_SPEECH -SpeechRecognitionError conditional=SCRIPTED_SPEECH -SpeechRecognitionEvent conditional=SCRIPTED_SPEECH +RTCIceCandidateEvent conditional=WEB_RTC +RTCDataChannelEvent conditional=WEB_RTC +RTCDTMFToneChangeEvent conditional=WEB_RTC +RTCTrackEvent conditional=WEB_RTC SpeechSynthesisEvent conditional=SPEECH_SYNTHESIS WebGLContextEvent conditional=WEBGL StorageEvent -SVGEvents interfaceName=Event, conditional=SVG -SVGZoomEvent conditional=SVG -SVGZoomEvents interfaceName=SVGZoomEvent, conditional=SVG +SVGEvents interfaceName=Event +SVGZoomEvent +SVGZoomEvents interfaceName=SVGZoomEvent IDBVersionChangeEvent conditional=INDEXED_DATABASE -TouchEvent conditional=TOUCH_EVENTS, runtimeConditional=touchEnabled +TouchEvent conditional=TOUCH_EVENTS DeviceMotionEvent conditional=DEVICE_ORIENTATION DeviceOrientationEvent conditional=DEVICE_ORIENTATION DeviceProximityEvent conditional=PROXIMITY_EVENTS OrientationEvent interfaceName=Event, conditional=ORIENTATION_EVENTS -MediaKeyEvent conditional=ENCRYPTED_MEDIA -MediaKeyMessageEvent conditional=ENCRYPTED_MEDIA_V2 -MediaKeyNeededEvent conditional=ENCRYPTED_MEDIA_V2 +WebKitMediaKeyMessageEvent conditional=LEGACY_ENCRYPTED_MEDIA +WebKitMediaKeyNeededEvent conditional=LEGACY_ENCRYPTED_MEDIA TrackEvent conditional=VIDEO_TRACK +AutocompleteErrorEvent conditional=REQUEST_AUTOCOMPLETE CSSFontFaceLoadEvent conditional=FONT_LOAD_EVENTS -SecurityPolicyViolationEvent conditional=CSP_NEXT -UIRequestEvent conditional=INDIE_UI -GestureEvent conditional=IOS_GESTURE_EVENTS -WebKitPlaybackTargetAvailabilityEvent conditional=IOS_AIRPLAY - +SecurityPolicyViolationEvent +GestureEvent conditional=IOS_GESTURE_EVENTS|MAC_GESTURE_EVENTS +WebKitPlaybackTargetAvailabilityEvent conditional=WIRELESS_PLAYBACK_TARGET +GamepadEvent conditional=GAMEPAD +OverconstrainedErrorEvent conditional=MEDIA_STREAM +MediaEncryptedEvent conditional=ENCRYPTED_MEDIA +MediaKeyMessageEvent conditional=ENCRYPTED_MEDIA diff --git a/Source/WebCore/dom/EventPath.cpp b/Source/WebCore/dom/EventPath.cpp new file mode 100644 index 000000000..3161eaa37 --- /dev/null +++ b/Source/WebCore/dom/EventPath.cpp @@ -0,0 +1,406 @@ +/* + * Copyright (C) 2013 Google Inc. All rights reserved. + * Copyright (C) 2013-2016 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "EventPath.h" + +#include "DOMWindow.h" +#include "Event.h" +#include "EventContext.h" +#include "EventNames.h" +#include "HTMLSlotElement.h" +#include "Node.h" +#include "PseudoElement.h" +#include "ShadowRoot.h" +#include "TouchEvent.h" + +namespace WebCore { + +class WindowEventContext final : public EventContext { +public: + WindowEventContext(Node&, DOMWindow&, EventTarget*); + void handleLocalEvents(Event&) const final; +}; + +WindowEventContext::WindowEventContext(Node& node, DOMWindow& currentTarget, EventTarget* target) + : EventContext(&node, ¤tTarget, target) +{ } + +void WindowEventContext::handleLocalEvents(Event& event) const +{ + event.setTarget(m_target.get()); + event.setCurrentTarget(m_currentTarget.get()); + m_currentTarget->fireEventListeners(event); +} + +static inline bool shouldEventCrossShadowBoundary(Event& event, ShadowRoot& shadowRoot, EventTarget& target) +{ + Node* targetNode = target.toNode(); + +#if ENABLE(FULLSCREEN_API) && ENABLE(VIDEO) + // Video-only full screen is a mode where we use the shadow DOM as an implementation + // detail that should not be detectable by the web content. + if (targetNode) { + if (Element* element = targetNode->document().webkitCurrentFullScreenElement()) { + // FIXME: We assume that if the full screen element is a media element that it's + // the video-only full screen. Both here and elsewhere. But that is probably wrong. + if (element->isMediaElement() && shadowRoot.host() == element) + return false; + } + } +#endif + + bool targetIsInShadowRoot = targetNode && &targetNode->treeScope().rootNode() == &shadowRoot; + return !targetIsInShadowRoot || event.composed(); +} + +static Node* nodeOrHostIfPseudoElement(Node* node) +{ + return is<PseudoElement>(*node) ? downcast<PseudoElement>(*node).hostElement() : node; +} + +class RelatedNodeRetargeter { +public: + RelatedNodeRetargeter(Node& relatedNode, Node& target); + + Node* currentNode(Node& currentTreeScope); + void moveToNewTreeScope(TreeScope* previousTreeScope, TreeScope& newTreeScope); + +private: + + Node* nodeInLowestCommonAncestor(); + void collectTreeScopes(); + +#if ASSERT_DISABLED + void checkConsistency(Node&) { } +#else + void checkConsistency(Node& currentTarget); +#endif + + Node& m_relatedNode; + Node* m_retargetedRelatedNode; + Vector<TreeScope*, 8> m_ancestorTreeScopes; + unsigned m_lowestCommonAncestorIndex { 0 }; + bool m_hasDifferentTreeRoot { false }; +}; + +EventPath::EventPath(Node& originalTarget, Event& event) +{ + bool isMouseOrFocusEvent = event.isMouseEvent() || event.isFocusEvent(); +#if ENABLE(TOUCH_EVENTS) + bool isTouchEvent = event.isTouchEvent(); +#endif + Node* node = nodeOrHostIfPseudoElement(&originalTarget); + Node* target = node ? eventTargetRespectingTargetRules(*node) : nullptr; + while (node) { + while (node) { + EventTarget* currentTarget = eventTargetRespectingTargetRules(*node); + + if (isMouseOrFocusEvent) + m_path.append(std::make_unique<MouseOrFocusEventContext>(node, currentTarget, target)); +#if ENABLE(TOUCH_EVENTS) + else if (isTouchEvent) + m_path.append(std::make_unique<TouchEventContext>(node, currentTarget, target)); +#endif + else + m_path.append(std::make_unique<EventContext>(node, currentTarget, target)); + + if (is<ShadowRoot>(*node)) + break; + + ContainerNode* parent = node->parentNode(); + if (UNLIKELY(!parent)) { + // https://dom.spec.whatwg.org/#interface-document + if (is<Document>(*node) && event.type() != eventNames().loadEvent) { + ASSERT(target); + if (auto* window = downcast<Document>(*node).domWindow()) + m_path.append(std::make_unique<WindowEventContext>(*node, *window, target)); + } + return; + } + + auto* shadowRootOfParent = parent->shadowRoot(); + if (UNLIKELY(shadowRootOfParent)) { + if (auto* assignedSlot = shadowRootOfParent->findAssignedSlot(*node)) { + // node is assigned to a slot. Continue dispatching the event at this slot. + parent = assignedSlot; + } + } + node = parent; + } + + bool exitingShadowTreeOfTarget = &target->treeScope() == &node->treeScope(); + ShadowRoot& shadowRoot = downcast<ShadowRoot>(*node); + if (!shouldEventCrossShadowBoundary(event, shadowRoot, originalTarget)) + return; + node = shadowRoot.host(); + if (exitingShadowTreeOfTarget) + target = eventTargetRespectingTargetRules(*node); + + } +} + +void EventPath::setRelatedTarget(Node& origin, EventTarget& relatedTarget) +{ + Node* relatedNode = relatedTarget.toNode(); + if (!relatedNode || m_path.isEmpty()) + return; + + RelatedNodeRetargeter retargeter(*relatedNode, *m_path[0]->node()); + + bool originIsRelatedTarget = &origin == relatedNode; + Node& rootNodeInOriginTreeScope = origin.treeScope().rootNode(); + TreeScope* previousTreeScope = nullptr; + size_t originalEventPathSize = m_path.size(); + for (unsigned contextIndex = 0; contextIndex < originalEventPathSize; contextIndex++) { + auto& ambgiousContext = *m_path[contextIndex]; + if (!is<MouseOrFocusEventContext>(ambgiousContext)) + continue; + auto& context = downcast<MouseOrFocusEventContext>(ambgiousContext); + + Node& currentTarget = *context.node(); + TreeScope& currentTreeScope = currentTarget.treeScope(); + if (UNLIKELY(previousTreeScope && ¤tTreeScope != previousTreeScope)) + retargeter.moveToNewTreeScope(previousTreeScope, currentTreeScope); + + Node* currentRelatedNode = retargeter.currentNode(currentTarget); + if (UNLIKELY(!originIsRelatedTarget && context.target() == currentRelatedNode)) { + m_path.shrink(contextIndex); + break; + } + + context.setRelatedTarget(currentRelatedNode); + + if (UNLIKELY(originIsRelatedTarget && context.node() == &rootNodeInOriginTreeScope)) { + m_path.shrink(contextIndex + 1); + break; + } + + previousTreeScope = ¤tTreeScope; + } +} + +#if ENABLE(TOUCH_EVENTS) +void EventPath::retargetTouch(TouchEventContext::TouchListType touchListType, const Touch& touch) +{ + EventTarget* eventTarget = touch.target(); + if (!eventTarget) + return; + + Node* targetNode = eventTarget->toNode(); + if (!targetNode) + return; + + RelatedNodeRetargeter retargeter(*targetNode, *m_path[0]->node()); + TreeScope* previousTreeScope = nullptr; + for (auto& context : m_path) { + Node& currentTarget = *context->node(); + TreeScope& currentTreeScope = currentTarget.treeScope(); + if (UNLIKELY(previousTreeScope && ¤tTreeScope != previousTreeScope)) + retargeter.moveToNewTreeScope(previousTreeScope, currentTreeScope); + + if (is<TouchEventContext>(*context)) { + Node* currentRelatedNode = retargeter.currentNode(currentTarget); + downcast<TouchEventContext>(*context).touchList(touchListType)->append(touch.cloneWithNewTarget(currentRelatedNode)); + } + + previousTreeScope = ¤tTreeScope; + } +} + +void EventPath::retargetTouchLists(const TouchEvent& touchEvent) +{ + if (touchEvent.touches()) { + for (size_t i = 0; i < touchEvent.touches()->length(); ++i) + retargetTouch(TouchEventContext::Touches, *touchEvent.touches()->item(i)); + } + + if (touchEvent.targetTouches()) { + for (size_t i = 0; i < touchEvent.targetTouches()->length(); ++i) + retargetTouch(TouchEventContext::TargetTouches, *touchEvent.targetTouches()->item(i)); + } + + if (touchEvent.changedTouches()) { + for (size_t i = 0; i < touchEvent.changedTouches()->length(); ++i) + retargetTouch(TouchEventContext::ChangedTouches, *touchEvent.changedTouches()->item(i)); + } +} +#endif + +bool EventPath::hasEventListeners(const AtomicString& eventType) const +{ + for (auto& context : m_path) { + if (context->node()->hasEventListeners(eventType)) + return true; + } + + return false; +} + +// https://dom.spec.whatwg.org/#dom-event-composedpath +Vector<EventTarget*> EventPath::computePathUnclosedToTarget(const EventTarget& target) const +{ + Vector<EventTarget*> path; + auto* targetNode = const_cast<EventTarget&>(target).toNode(); + if (!targetNode) { + auto* domWindow = const_cast<EventTarget&>(target).toDOMWindow(); + if (!domWindow) + return path; + targetNode = domWindow->document(); + ASSERT(targetNode); + } + for (auto& context : m_path) { + if (auto* nodeInPath = context->currentTarget()->toNode()) { + if (!targetNode->isClosedShadowHidden(*nodeInPath)) + path.append(context->currentTarget()); + } else + path.append(context->currentTarget()); + } + return path; +} + +static Node* moveOutOfAllShadowRoots(Node& startingNode) +{ + Node* node = &startingNode; + while (node->isInShadowTree()) + node = downcast<ShadowRoot>(node->treeScope().rootNode()).host(); + return node; +} + +RelatedNodeRetargeter::RelatedNodeRetargeter(Node& relatedNode, Node& target) + : m_relatedNode(relatedNode) + , m_retargetedRelatedNode(&relatedNode) +{ + auto& targetTreeScope = target.treeScope(); + TreeScope* currentTreeScope = &m_relatedNode.treeScope(); + if (LIKELY(currentTreeScope == &targetTreeScope && target.isConnected() && m_relatedNode.isConnected())) + return; + + if (¤tTreeScope->documentScope() != &targetTreeScope.documentScope()) { + m_hasDifferentTreeRoot = true; + m_retargetedRelatedNode = nullptr; + return; + } + if (relatedNode.isConnected() != target.isConnected()) { + m_hasDifferentTreeRoot = true; + m_retargetedRelatedNode = moveOutOfAllShadowRoots(relatedNode); + return; + } + + collectTreeScopes(); + + // FIXME: We should collect this while constructing the event path. + Vector<TreeScope*, 8> targetTreeScopeAncestors; + for (TreeScope* currentTreeScope = &targetTreeScope; currentTreeScope; currentTreeScope = currentTreeScope->parentTreeScope()) + targetTreeScopeAncestors.append(currentTreeScope); + ASSERT_WITH_SECURITY_IMPLICATION(!targetTreeScopeAncestors.isEmpty()); + + unsigned i = m_ancestorTreeScopes.size(); + unsigned j = targetTreeScopeAncestors.size(); + ASSERT_WITH_SECURITY_IMPLICATION(m_ancestorTreeScopes.last() == targetTreeScopeAncestors.last()); + while (m_ancestorTreeScopes[i - 1] == targetTreeScopeAncestors[j - 1]) { + i--; + j--; + if (!i || !j) + break; + } + + bool lowestCommonAncestorIsDocumentScope = i + 1 == m_ancestorTreeScopes.size(); + if (lowestCommonAncestorIsDocumentScope && !relatedNode.isConnected() && !target.isConnected()) { + Node& relatedNodeAncestorInDocumentScope = i ? *downcast<ShadowRoot>(m_ancestorTreeScopes[i - 1]->rootNode()).shadowHost() : relatedNode; + Node& targetAncestorInDocumentScope = j ? *downcast<ShadowRoot>(targetTreeScopeAncestors[j - 1]->rootNode()).shadowHost() : target; + if (&targetAncestorInDocumentScope.rootNode() != &relatedNodeAncestorInDocumentScope.rootNode()) { + m_hasDifferentTreeRoot = true; + m_retargetedRelatedNode = moveOutOfAllShadowRoots(relatedNode); + return; + } + } + + m_lowestCommonAncestorIndex = i; + m_retargetedRelatedNode = nodeInLowestCommonAncestor(); +} + +inline Node* RelatedNodeRetargeter::currentNode(Node& currentTarget) +{ + checkConsistency(currentTarget); + return m_retargetedRelatedNode; +} + +void RelatedNodeRetargeter::moveToNewTreeScope(TreeScope* previousTreeScope, TreeScope& newTreeScope) +{ + if (m_hasDifferentTreeRoot) + return; + + auto& currentRelatedNodeScope = m_retargetedRelatedNode->treeScope(); + if (previousTreeScope != ¤tRelatedNodeScope) { + // currentRelatedNode is still outside our shadow tree. New tree scope may contain currentRelatedNode + // but there is no need to re-target it. Moving into a slot (thereby a deeper shadow tree) doesn't matter. + return; + } + + bool enteredSlot = newTreeScope.parentTreeScope() == previousTreeScope; + if (enteredSlot) { + if (m_lowestCommonAncestorIndex) { + if (m_ancestorTreeScopes.isEmpty()) + collectTreeScopes(); + bool relatedNodeIsInSlot = m_ancestorTreeScopes[m_lowestCommonAncestorIndex - 1] == &newTreeScope; + if (relatedNodeIsInSlot) { + m_lowestCommonAncestorIndex--; + m_retargetedRelatedNode = nodeInLowestCommonAncestor(); + ASSERT(&newTreeScope == &m_retargetedRelatedNode->treeScope()); + } + } else + ASSERT(m_retargetedRelatedNode == &m_relatedNode); + } else { + ASSERT(previousTreeScope->parentTreeScope() == &newTreeScope); + m_lowestCommonAncestorIndex++; + ASSERT_WITH_SECURITY_IMPLICATION(m_ancestorTreeScopes.isEmpty() || m_lowestCommonAncestorIndex < m_ancestorTreeScopes.size()); + m_retargetedRelatedNode = downcast<ShadowRoot>(currentRelatedNodeScope.rootNode()).host(); + ASSERT(&newTreeScope == &m_retargetedRelatedNode->treeScope()); + } +} + +inline Node* RelatedNodeRetargeter::nodeInLowestCommonAncestor() +{ + if (!m_lowestCommonAncestorIndex) + return &m_relatedNode; + auto& rootNode = m_ancestorTreeScopes[m_lowestCommonAncestorIndex - 1]->rootNode(); + return downcast<ShadowRoot>(rootNode).host(); +} + +void RelatedNodeRetargeter::collectTreeScopes() +{ + ASSERT(m_ancestorTreeScopes.isEmpty()); + for (TreeScope* currentTreeScope = &m_relatedNode.treeScope(); currentTreeScope; currentTreeScope = currentTreeScope->parentTreeScope()) + m_ancestorTreeScopes.append(currentTreeScope); + ASSERT_WITH_SECURITY_IMPLICATION(!m_ancestorTreeScopes.isEmpty()); +} + +#if !ASSERT_DISABLED +void RelatedNodeRetargeter::checkConsistency(Node& currentTarget) +{ + if (!m_retargetedRelatedNode) + return; + ASSERT(!currentTarget.isClosedShadowHidden(*m_retargetedRelatedNode)); + ASSERT(m_retargetedRelatedNode == ¤tTarget.treeScope().retargetToScope(m_relatedNode)); +} +#endif + +} diff --git a/Source/WebCore/dom/EventPath.h b/Source/WebCore/dom/EventPath.h new file mode 100644 index 000000000..a58af5a22 --- /dev/null +++ b/Source/WebCore/dom/EventPath.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2013 Google Inc. All rights reserved. + * Copyright (C) 2013-2016 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include "EventContext.h" +#include "PseudoElement.h" +#include "SVGElement.h" +#include "SVGUseElement.h" +#include <wtf/Forward.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class EventPath { +public: + EventPath(Node& origin, Event&); + + bool isEmpty() const { return m_path.isEmpty(); } + size_t size() const { return m_path.size(); } + const EventContext& contextAt(size_t i) const { return *m_path[i]; } + EventContext& contextAt(size_t i) { return *m_path[i]; } + +#if ENABLE(TOUCH_EVENTS) + void retargetTouchLists(const TouchEvent&); +#endif + void setRelatedTarget(Node& origin, EventTarget&); + + bool hasEventListeners(const AtomicString& eventType) const; + + EventContext* lastContextIfExists() { return m_path.isEmpty() ? nullptr : m_path.last().get(); } + + Vector<EventTarget*> computePathUnclosedToTarget(const EventTarget&) const; + + static Node* eventTargetRespectingTargetRules(Node& referenceNode) + { + if (is<PseudoElement>(referenceNode)) + return downcast<PseudoElement>(referenceNode).hostElement(); + + // Events sent to elements inside an SVG use element's shadow tree go to the use element. + if (is<SVGElement>(referenceNode)) { + if (auto* useElement = downcast<SVGElement>(referenceNode).correspondingUseElement()) + return useElement; + } + + return &referenceNode; + } + +private: +#if ENABLE(TOUCH_EVENTS) + void retargetTouch(TouchEventContext::TouchListType, const Touch&); +#endif + + Vector<std::unique_ptr<EventContext>, 32> m_path; +}; + +} // namespace WebCore diff --git a/Source/WebCore/dom/EventQueue.h b/Source/WebCore/dom/EventQueue.h index 2906f80e9..1ba06c52c 100644 --- a/Source/WebCore/dom/EventQueue.h +++ b/Source/WebCore/dom/EventQueue.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,8 +24,7 @@ * */ -#ifndef EventQueue_h -#define EventQueue_h +#pragma once #include <wtf/Forward.h> @@ -36,11 +35,9 @@ class Event; class EventQueue { public: virtual ~EventQueue() { } - virtual bool enqueueEvent(PassRefPtr<Event>) = 0; + virtual bool enqueueEvent(Ref<Event>&&) = 0; virtual bool cancelEvent(Event&) = 0; virtual void close() = 0; // Discard accumulated events and all future events. No events will be dispatched after this. }; -} - -#endif // EventQueue_h +} // namespace WebCore diff --git a/Source/WebCore/dom/EventSender.h b/Source/WebCore/dom/EventSender.h index 2cb5aaf18..872d85a1a 100644 --- a/Source/WebCore/dom/EventSender.h +++ b/Source/WebCore/dom/EventSender.h @@ -23,8 +23,7 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef EventSender_h -#define EventSender_h +#pragma once #include "Timer.h" #include <wtf/Vector.h> @@ -37,52 +36,50 @@ public: explicit EventSender(const AtomicString& eventType); const AtomicString& eventType() const { return m_eventType; } - void dispatchEventSoon(T*); - void cancelEvent(T*); + void dispatchEventSoon(T&); + void cancelEvent(T&); void dispatchPendingEvents(); #ifndef NDEBUG - bool hasPendingEvents(T* sender) const + bool hasPendingEvents(T& sender) const { - return m_dispatchSoonList.find(sender) != notFound || m_dispatchingList.find(sender) != notFound; + return m_dispatchSoonList.find(&sender) != notFound || m_dispatchingList.find(&sender) != notFound; } #endif private: - void timerFired(Timer<EventSender<T>>&) { dispatchPendingEvents(); } + void timerFired() { dispatchPendingEvents(); } AtomicString m_eventType; - Timer<EventSender<T>> m_timer; + Timer m_timer; Vector<T*> m_dispatchSoonList; Vector<T*> m_dispatchingList; }; template<typename T> EventSender<T>::EventSender(const AtomicString& eventType) : m_eventType(eventType) - , m_timer(this, &EventSender::timerFired) + , m_timer(*this, &EventSender::timerFired) { } -template<typename T> void EventSender<T>::dispatchEventSoon(T* sender) +template<typename T> void EventSender<T>::dispatchEventSoon(T& sender) { - m_dispatchSoonList.append(sender); + m_dispatchSoonList.append(&sender); if (!m_timer.isActive()) m_timer.startOneShot(0); } -template<typename T> void EventSender<T>::cancelEvent(T* sender) +template<typename T> void EventSender<T>::cancelEvent(T& sender) { // Remove instances of this sender from both lists. // Use loops because we allow multiple instances to get into the lists. - size_t size = m_dispatchSoonList.size(); - for (size_t i = 0; i < size; ++i) { - if (m_dispatchSoonList[i] == sender) - m_dispatchSoonList[i] = 0; + for (auto& event : m_dispatchSoonList) { + if (event == &sender) + event = nullptr; } - size = m_dispatchingList.size(); - for (size_t i = 0; i < size; ++i) { - if (m_dispatchingList[i] == sender) - m_dispatchingList[i] = 0; + for (auto& event : m_dispatchingList) { + if (event == &sender) + event = nullptr; } } @@ -99,10 +96,9 @@ template<typename T> void EventSender<T>::dispatchPendingEvents() m_dispatchSoonList.checkConsistency(); m_dispatchingList.swap(m_dispatchSoonList); - size_t size = m_dispatchingList.size(); - for (size_t i = 0; i < size; ++i) { - if (T* sender = m_dispatchingList[i]) { - m_dispatchingList[i] = 0; + for (auto& event : m_dispatchingList) { + if (T* sender = event) { + event = nullptr; sender->dispatchPendingEvent(this); } } @@ -110,5 +106,3 @@ template<typename T> void EventSender<T>::dispatchPendingEvents() } } // namespace WebCore - -#endif // EventSender_h diff --git a/Source/WebCore/dom/EventTarget.cpp b/Source/WebCore/dom/EventTarget.cpp index d1a2e0cec..f18722904 100644 --- a/Source/WebCore/dom/EventTarget.cpp +++ b/Source/WebCore/dom/EventTarget.cpp @@ -2,7 +2,7 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2004-2017 Apple Inc. All rights reserved. * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org) * (C) 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org> * @@ -15,10 +15,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -32,12 +32,19 @@ #include "config.h" #include "EventTarget.h" -#include "EventException.h" +#include "DOMWrapperWorld.h" +#include "EventNames.h" +#include "ExceptionCode.h" #include "InspectorInstrumentation.h" +#include "JSEventListener.h" +#include "NoEventDispatchAssertion.h" #include "ScriptController.h" +#include "WebKitAnimationEvent.h" #include "WebKitTransitionEvent.h" #include <wtf/MainThread.h> +#include <wtf/NeverDestroyed.h> #include <wtf/Ref.h> +#include <wtf/SetForScope.h> #include <wtf/StdLibExtras.h> #include <wtf/Vector.h> @@ -45,26 +52,14 @@ using namespace WTF; namespace WebCore { -EventTargetData::EventTargetData() -{ -} - -EventTargetData::~EventTargetData() -{ -} - -EventTarget::~EventTarget() -{ -} - Node* EventTarget::toNode() { - return 0; + return nullptr; } DOMWindow* EventTarget::toDOMWindow() { - return 0; + return nullptr; } bool EventTarget::isMessagePort() const @@ -72,79 +67,87 @@ bool EventTarget::isMessagePort() const return false; } -bool EventTarget::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture) +bool EventTarget::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options) { - return ensureEventTargetData().eventListenerMap.add(eventType, listener, useCapture); + return ensureEventTargetData().eventListenerMap.add(eventType, WTFMove(listener), { options.capture, options.passive, options.once }); } -bool EventTarget::removeEventListener(const AtomicString& eventType, EventListener* listener, bool useCapture) +void EventTarget::addEventListenerForBindings(const AtomicString& eventType, RefPtr<EventListener>&& listener, AddEventListenerOptionsOrBoolean&& variant) { - EventTargetData* d = eventTargetData(); - if (!d) - return false; + if (!listener) + return; - size_t indexOfRemovedListener; + auto visitor = WTF::makeVisitor([&](const AddEventListenerOptions& options) { + addEventListener(eventType, listener.releaseNonNull(), options); + }, [&](bool capture) { + addEventListener(eventType, listener.releaseNonNull(), capture); + }); - if (!d->eventListenerMap.remove(eventType, listener, useCapture, indexOfRemovedListener)) - return false; + WTF::visit(visitor, variant); +} - // Notify firing events planning to invoke the listener at 'index' that - // they have one less listener to invoke. - if (!d->firingEventIterators) - return true; - for (size_t i = 0; i < d->firingEventIterators->size(); ++i) { - FiringEventIterator& firingIterator = d->firingEventIterators->at(i); - if (eventType != firingIterator.eventType) - continue; +void EventTarget::removeEventListenerForBindings(const AtomicString& eventType, RefPtr<EventListener>&& listener, ListenerOptionsOrBoolean&& variant) +{ + if (!listener) + return; - if (indexOfRemovedListener >= firingIterator.size) - continue; + auto visitor = WTF::makeVisitor([&](const ListenerOptions& options) { + removeEventListener(eventType, *listener, options); + }, [&](bool capture) { + removeEventListener(eventType, *listener, capture); + }); - --firingIterator.size; - if (indexOfRemovedListener <= firingIterator.iterator) - --firingIterator.iterator; - } + WTF::visit(visitor, variant); +} - return true; +bool EventTarget::removeEventListener(const AtomicString& eventType, EventListener& listener, const ListenerOptions& options) +{ + auto* data = eventTargetData(); + return data && data->eventListenerMap.remove(eventType, listener, options.capture); } -bool EventTarget::setAttributeEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener) +bool EventTarget::setAttributeEventListener(const AtomicString& eventType, RefPtr<EventListener>&& listener, DOMWrapperWorld& isolatedWorld) { - clearAttributeEventListener(eventType); - if (!listener) + auto* existingListener = attributeEventListener(eventType, isolatedWorld); + if (!listener) { + if (existingListener) + removeEventListener(eventType, *existingListener, false); return false; - return addEventListener(eventType, listener, false); + } + if (existingListener) { + eventTargetData()->eventListenerMap.replace(eventType, *existingListener, listener.releaseNonNull(), { }); + return true; + } + return addEventListener(eventType, listener.releaseNonNull()); } -EventListener* EventTarget::getAttributeEventListener(const AtomicString& eventType) +EventListener* EventTarget::attributeEventListener(const AtomicString& eventType, DOMWrapperWorld& isolatedWorld) { - const EventListenerVector& entry = getEventListeners(eventType); - for (size_t i = 0; i < entry.size(); ++i) { - if (entry[i].listener->isAttribute()) - return entry[i].listener.get(); + for (auto& eventListener : eventListeners(eventType)) { + auto& listener = eventListener->callback(); + if (!listener.isAttribute()) + continue; + + auto& listenerWorld = downcast<JSEventListener>(listener).isolatedWorld(); + if (&listenerWorld == &isolatedWorld) + return &listener; } - return 0; + + return nullptr; } -bool EventTarget::clearAttributeEventListener(const AtomicString& eventType) +bool EventTarget::hasActiveEventListeners(const AtomicString& eventType) const { - EventListener* listener = getAttributeEventListener(eventType); - if (!listener) - return false; - return removeEventListener(eventType, listener, false); + auto* data = eventTargetData(); + return data && data->eventListenerMap.containsActive(eventType); } -bool EventTarget::dispatchEvent(PassRefPtr<Event> event, ExceptionCode& ec) +ExceptionOr<bool> EventTarget::dispatchEventForBindings(Event& event) { - if (!event || event->type().isEmpty()) { - ec = EventException::UNSPECIFIED_EVENT_TYPE_ERR; - return false; - } + event.setUntrusted(); - if (event->isBeingDispatched()) { - ec = EventException::DISPATCH_REQUEST_ERR; - return false; - } + if (!event.isInitialized() || event.isBeingDispatched()) + return Exception { INVALID_STATE_ERR }; if (!scriptExecutionContext()) return false; @@ -152,13 +155,17 @@ bool EventTarget::dispatchEvent(PassRefPtr<Event> event, ExceptionCode& ec) return dispatchEvent(event); } -bool EventTarget::dispatchEvent(PassRefPtr<Event> event) +bool EventTarget::dispatchEvent(Event& event) { - event->setTarget(this); - event->setCurrentTarget(this); - event->setEventPhase(Event::AT_TARGET); - bool defaultPrevented = fireEventListeners(event.get()); - event->setEventPhase(0); + ASSERT(event.isInitialized()); + ASSERT(!event.isBeingDispatched()); + + event.setTarget(this); + event.setCurrentTarget(this); + event.setEventPhase(Event::AT_TARGET); + bool defaultPrevented = fireEventListeners(event); + event.resetPropagationFlags(); + event.setEventPhase(Event::NONE); return defaultPrevented; } @@ -166,164 +173,130 @@ void EventTarget::uncaughtExceptionInEventHandler() { } -static const AtomicString& legacyType(const Event* event) +static const AtomicString& legacyType(const Event& event) { - if (event->type() == eventNames().transitionendEvent) - return eventNames().webkitTransitionEndEvent; + if (event.type() == eventNames().animationendEvent) + return eventNames().webkitAnimationEndEvent; - if (event->type() == eventNames().wheelEvent) - return eventNames().mousewheelEvent; + if (event.type() == eventNames().animationstartEvent) + return eventNames().webkitAnimationStartEvent; - return emptyAtom; -} - -static inline bool shouldObserveLegacyType(const AtomicString& legacyTypeName, bool hasLegacyTypeListeners, bool hasNewTypeListeners, FeatureObserver::Feature& feature) -{ - if (legacyTypeName == eventNames().webkitTransitionEndEvent) { - if (hasLegacyTypeListeners) { - if (hasNewTypeListeners) - feature = FeatureObserver::PrefixedAndUnprefixedTransitionEndEvent; - else - feature = FeatureObserver::PrefixedTransitionEndEvent; - } else { - ASSERT(hasNewTypeListeners); - feature = FeatureObserver::UnprefixedTransitionEndEvent; - } - return true; - } - return false; -} - -void EventTarget::setupLegacyTypeObserverIfNeeded(const AtomicString& legacyTypeName, bool hasLegacyTypeListeners, bool hasNewTypeListeners) -{ - ASSERT(!legacyTypeName.isEmpty()); - ASSERT(hasLegacyTypeListeners || hasNewTypeListeners); + if (event.type() == eventNames().animationiterationEvent) + return eventNames().webkitAnimationIterationEvent; - ScriptExecutionContext* context = scriptExecutionContext(); - if (!context || !context->isDocument()) - return; + if (event.type() == eventNames().transitionendEvent) + return eventNames().webkitTransitionEndEvent; - Document* document = toDocument(context); - if (!document->domWindow()) - return; + // FIXME: This legacy name is not part of the specification (https://dom.spec.whatwg.org/#dispatching-events). + if (event.type() == eventNames().wheelEvent) + return eventNames().mousewheelEvent; - FeatureObserver::Feature feature; - if (shouldObserveLegacyType(legacyTypeName, hasLegacyTypeListeners, hasNewTypeListeners, feature)) - FeatureObserver::observe(document->domWindow(), feature); + return nullAtom; } -bool EventTarget::fireEventListeners(Event* event) +bool EventTarget::fireEventListeners(Event& event) { - ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden()); - ASSERT(event && !event->type().isEmpty()); + ASSERT_WITH_SECURITY_IMPLICATION(NoEventDispatchAssertion::isEventAllowedInMainThread()); + ASSERT(event.isInitialized()); - EventTargetData* d = eventTargetData(); - if (!d) + auto* data = eventTargetData(); + if (!data) return true; - EventListenerVector* legacyListenersVector = 0; - const AtomicString& legacyTypeName = legacyType(event); - if (!legacyTypeName.isEmpty()) - legacyListenersVector = d->eventListenerMap.find(legacyTypeName); - - EventListenerVector* listenersVector = d->eventListenerMap.find(event->type()); - - if (listenersVector) - fireEventListeners(event, d, *listenersVector); - else if (legacyListenersVector) { - AtomicString typeName = event->type(); - event->setType(legacyTypeName); - fireEventListeners(event, d, *legacyListenersVector); - event->setType(typeName); + SetForScope<bool> firingEventListenersScope(data->isFiringEventListeners, true); + + if (auto* listenersVector = data->eventListenerMap.find(event.type())) { + fireEventListeners(event, *listenersVector); + return !event.defaultPrevented(); } - if (!legacyTypeName.isEmpty() && (legacyListenersVector || listenersVector)) - setupLegacyTypeObserverIfNeeded(legacyTypeName, !!legacyListenersVector, !!listenersVector); + // Only fall back to legacy types for trusted events. + if (!event.isTrusted()) + return !event.defaultPrevented(); - return !event->defaultPrevented(); + const AtomicString& legacyTypeName = legacyType(event); + if (!legacyTypeName.isNull()) { + if (auto* legacyListenersVector = data->eventListenerMap.find(legacyTypeName)) { + AtomicString typeName = event.type(); + event.setType(legacyTypeName); + fireEventListeners(event, *legacyListenersVector); + event.setType(typeName); + } + } + return !event.defaultPrevented(); } - -void EventTarget::fireEventListeners(Event* event, EventTargetData* d, EventListenerVector& entry) -{ - Ref<EventTarget> protect(*this); - - // Fire all listeners registered for this event. Don't fire listeners removed during event dispatch. - // Also, don't fire event listeners added during event dispatch. Conveniently, all new event listeners will be added - // after or at index |size|, so iterating up to (but not including) |size| naturally excludes new event listeners. - bool userEventWasHandled = false; - size_t i = 0; - size_t size = entry.size(); - if (!d->firingEventIterators) - d->firingEventIterators = adoptPtr(new FiringEventIteratorVector); - d->firingEventIterators->append(FiringEventIterator(event->type(), i, size)); +// Intentionally creates a copy of the listeners vector to avoid event listeners added after this point from being run. +// Note that removal still has an effect due to the removed field in RegisteredEventListener. +void EventTarget::fireEventListeners(Event& event, EventListenerVector listeners) +{ + Ref<EventTarget> protectedThis(*this); + ASSERT(!listeners.isEmpty()); - ScriptExecutionContext* context = scriptExecutionContext(); - Document* document = nullptr; + auto* context = scriptExecutionContext(); + bool contextIsDocument = is<Document>(context); InspectorInstrumentationCookie willDispatchEventCookie; - if (context && context->isDocument()) { - document = toDocument(context); - willDispatchEventCookie = InspectorInstrumentation::willDispatchEvent(document, *event, size > 0); - } + if (contextIsDocument) + willDispatchEventCookie = InspectorInstrumentation::willDispatchEvent(downcast<Document>(*context), event, true); - for (; i < size; ++i) { - RegisteredEventListener& registeredListener = entry[i]; - if (event->eventPhase() == Event::CAPTURING_PHASE && !registeredListener.useCapture) + for (auto& registeredListener : listeners) { + if (UNLIKELY(registeredListener->wasRemoved())) continue; - if (event->eventPhase() == Event::BUBBLING_PHASE && registeredListener.useCapture) + + if (event.eventPhase() == Event::CAPTURING_PHASE && !registeredListener->useCapture()) + continue; + if (event.eventPhase() == Event::BUBBLING_PHASE && registeredListener->useCapture()) continue; // If stopImmediatePropagation has been called, we just break out immediately, without // handling any more events on this target. - if (event->immediatePropagationStopped()) + if (event.immediatePropagationStopped()) break; - InspectorInstrumentationCookie cookie = InspectorInstrumentation::willHandleEvent(context, event); - // To match Mozilla, the AT_TARGET phase fires both capturing and bubbling - // event listeners, even though that violates some versions of the DOM spec. - registeredListener.listener->handleEvent(context, event); - if (!userEventWasHandled && ScriptController::processingUserGesture()) - userEventWasHandled = true; - InspectorInstrumentation::didHandleEvent(cookie); + // Do this before invocation to avoid reentrancy issues. + if (registeredListener->isOnce()) + removeEventListener(event.type(), registeredListener->callback(), ListenerOptions(registeredListener->useCapture())); + + if (registeredListener->isPassive()) + event.setInPassiveListener(true); + + InspectorInstrumentation::willHandleEvent(context, event); + registeredListener->callback().handleEvent(context, &event); + + if (registeredListener->isPassive()) + event.setInPassiveListener(false); } - d->firingEventIterators->removeLast(); - if (userEventWasHandled && document) - document->resetLastHandledUserGestureTimestamp(); - if (document) + if (contextIsDocument) InspectorInstrumentation::didDispatchEvent(willDispatchEventCookie); } -const EventListenerVector& EventTarget::getEventListeners(const AtomicString& eventType) +const EventListenerVector& EventTarget::eventListeners(const AtomicString& eventType) { - DEFINE_STATIC_LOCAL(EventListenerVector, emptyVector, ()); - - EventTargetData* d = eventTargetData(); - if (!d) - return emptyVector; - - EventListenerVector* listenerVector = d->eventListenerMap.find(eventType); - if (!listenerVector) - return emptyVector; - - return *listenerVector; + auto* data = eventTargetData(); + auto* listenerVector = data ? data->eventListenerMap.find(eventType) : nullptr; + static NeverDestroyed<EventListenerVector> emptyVector; + return listenerVector ? *listenerVector : emptyVector.get(); } void EventTarget::removeAllEventListeners() { - EventTargetData* d = eventTargetData(); - if (!d) + auto* data = eventTargetData(); + if (!data) return; - d->eventListenerMap.clear(); - - // Notify firing events planning to invoke the listener at 'index' that - // they have one less listener to invoke. - if (d->firingEventIterators) { - for (size_t i = 0; i < d->firingEventIterators->size(); ++i) { - d->firingEventIterators->at(i).iterator = 0; - d->firingEventIterators->at(i).size = 0; - } - } + data->eventListenerMap.clear(); +} + +void EventTarget::visitJSEventListeners(JSC::SlotVisitor& visitor) +{ + EventTargetData* data = eventTargetDataConcurrently(); + if (!data) + return; + + auto locker = holdLock(data->eventListenerMap.lock()); + EventListenerIterator iterator(&data->eventListenerMap); + while (auto* listener = iterator.nextListener()) + listener->visitJSFunction(visitor); } } // namespace WebCore diff --git a/Source/WebCore/dom/EventTarget.h b/Source/WebCore/dom/EventTarget.h index e6d6b29cc..892d4b812 100644 --- a/Source/WebCore/dom/EventTarget.h +++ b/Source/WebCore/dom/EventTarget.h @@ -2,7 +2,7 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2004-2017 Apple Inc. All rights reserved. * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org) * (C) 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org> * @@ -15,227 +15,164 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef EventTarget_h -#define EventTarget_h +#pragma once #include "EventListenerMap.h" -#include "EventNames.h" #include "EventTargetInterfaces.h" +#include "ExceptionOr.h" +#include "ScriptWrappable.h" +#include <memory> #include <wtf/Forward.h> -#include <wtf/HashMap.h> -#include <wtf/text/AtomicStringHash.h> +#include <wtf/Variant.h> namespace WebCore { - class AudioNode; - class AudioContext; - class AudioTrackList; - class DedicatedWorkerGlobalScope; - class DOMApplicationCache; - class DOMWindow; - class Event; - class EventListener; - class EventSource; - class FileReader; - class FileWriter; - class IDBDatabase; - class IDBRequest; - class IDBTransaction; - class ScriptProcessorNode; - class MediaController; - class MediaStream; - class MessagePort; - class Node; - class Notification; - class SVGElementInstance; - class ScriptExecutionContext; - class SharedWorker; - class SharedWorkerGlobalScope; - class TextTrack; - class TextTrackCue; - class VideoTrackList; - class WebSocket; - class WebKitNamedFlow; - class Worker; - class XMLHttpRequest; - class XMLHttpRequestUpload; - - typedef int ExceptionCode; - - struct FiringEventIterator { - FiringEventIterator(const AtomicString& eventType, size_t& iterator, size_t& size) - : eventType(eventType) - , iterator(iterator) - , size(size) - { - } - - const AtomicString& eventType; - size_t& iterator; - size_t& size; - }; - typedef Vector<FiringEventIterator, 1> FiringEventIteratorVector; +class DOMWindow; +class DOMWrapperWorld; +class Node; - struct EventTargetData { - WTF_MAKE_NONCOPYABLE(EventTargetData); WTF_MAKE_FAST_ALLOCATED; - public: - EventTargetData(); - ~EventTargetData(); +struct EventTargetData { + WTF_MAKE_NONCOPYABLE(EventTargetData); WTF_MAKE_FAST_ALLOCATED; +public: + EventTargetData() = default; + EventListenerMap eventListenerMap; + bool isFiringEventListeners { false }; +}; - EventListenerMap eventListenerMap; - OwnPtr<FiringEventIteratorVector> firingEventIterators; - }; +enum EventTargetInterface { - enum EventTargetInterface { +#define DOM_EVENT_INTERFACE_DECLARE(name) name##EventTargetInterfaceType, +DOM_EVENT_TARGET_INTERFACES_FOR_EACH(DOM_EVENT_INTERFACE_DECLARE) +#undef DOM_EVENT_INTERFACE_DECLARE - #define DOM_EVENT_INTERFACE_DECLARE(name) name##EventTargetInterfaceType, - DOM_EVENT_TARGET_INTERFACES_FOR_EACH(DOM_EVENT_INTERFACE_DECLARE) - #undef DOM_EVENT_INTERFACE_DECLARE +}; - }; +class EventTarget : public ScriptWrappable { +public: + void ref() { refEventTarget(); } + void deref() { derefEventTarget(); } + + virtual EventTargetInterface eventTargetInterface() const = 0; + virtual ScriptExecutionContext* scriptExecutionContext() const = 0; - class EventTarget { - public: - void ref() { refEventTarget(); } - void deref() { derefEventTarget(); } - - virtual EventTargetInterface eventTargetInterface() const = 0; - virtual ScriptExecutionContext* scriptExecutionContext() const = 0; - - virtual Node* toNode(); - virtual DOMWindow* toDOMWindow(); - virtual bool isMessagePort() const; - - virtual bool addEventListener(const AtomicString& eventType, PassRefPtr<EventListener>, bool useCapture); - virtual bool removeEventListener(const AtomicString& eventType, EventListener*, bool useCapture); - virtual void removeAllEventListeners(); - virtual bool dispatchEvent(PassRefPtr<Event>); - bool dispatchEvent(PassRefPtr<Event>, ExceptionCode&); // DOM API - virtual void uncaughtExceptionInEventHandler(); - - // Used for legacy "onEvent" attribute APIs. - bool setAttributeEventListener(const AtomicString& eventType, PassRefPtr<EventListener>); - bool clearAttributeEventListener(const AtomicString& eventType); - EventListener* getAttributeEventListener(const AtomicString& eventType); - - bool hasEventListeners() const; - bool hasEventListeners(const AtomicString& eventType); - bool hasCapturingEventListeners(const AtomicString& eventType); - const EventListenerVector& getEventListeners(const AtomicString& eventType); - - bool fireEventListeners(Event*); - bool isFiringEventListeners(); - - void visitJSEventListeners(JSC::SlotVisitor&); - void invalidateJSEventListeners(JSC::JSObject*); - - protected: - virtual ~EventTarget(); - - virtual EventTargetData* eventTargetData() = 0; - virtual EventTargetData& ensureEventTargetData() = 0; - - private: - virtual void refEventTarget() = 0; - virtual void derefEventTarget() = 0; - - void fireEventListeners(Event*, EventTargetData*, EventListenerVector&); - void setupLegacyTypeObserverIfNeeded(const AtomicString& legacyTypeName, bool hasLegacyTypeListeners, bool hasNewTypeListeners); - - friend class EventListenerIterator; + virtual Node* toNode(); + virtual DOMWindow* toDOMWindow(); + virtual bool isMessagePort() const; + + struct ListenerOptions { + ListenerOptions(bool capture = false) + : capture(capture) + { } + + bool capture; }; - class EventTargetWithInlineData : public EventTarget { - protected: - virtual EventTargetData* eventTargetData() override final { return &m_eventTargetData; } - virtual EventTargetData& ensureEventTargetData() override final { return m_eventTargetData; } - private: - EventTargetData m_eventTargetData; + struct AddEventListenerOptions : ListenerOptions { + AddEventListenerOptions(bool capture = false, bool passive = false, bool once = false) + : ListenerOptions(capture) + , passive(passive) + , once(once) + { } + + bool passive; + bool once; }; - // FIXME: These macros should be split into separate DEFINE and DECLARE - // macros to avoid causing so many header includes. - #define DEFINE_ATTRIBUTE_EVENT_LISTENER(attribute) \ - EventListener* on##attribute() { return getAttributeEventListener(eventNames().attribute##Event); } \ - void setOn##attribute(PassRefPtr<EventListener> listener) { setAttributeEventListener(eventNames().attribute##Event, listener); } \ - - #define DECLARE_VIRTUAL_ATTRIBUTE_EVENT_LISTENER(attribute) \ - virtual EventListener* on##attribute(); \ - virtual void setOn##attribute(PassRefPtr<EventListener> listener); \ - - #define DEFINE_VIRTUAL_ATTRIBUTE_EVENT_LISTENER(type, attribute) \ - EventListener* type::on##attribute() { return getAttributeEventListener(eventNames().attribute##Event); } \ - void type::setOn##attribute(PassRefPtr<EventListener> listener) { setAttributeEventListener(eventNames().attribute##Event, listener); } \ - - #define DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(attribute) \ - EventListener* on##attribute() { return document().getWindowAttributeEventListener(eventNames().attribute##Event); } \ - void setOn##attribute(PassRefPtr<EventListener> listener) { document().setWindowAttributeEventListener(eventNames().attribute##Event, listener); } \ - - #define DEFINE_MAPPED_ATTRIBUTE_EVENT_LISTENER(attribute, eventName) \ - EventListener* on##attribute() { return getAttributeEventListener(eventNames().eventName##Event); } \ - void setOn##attribute(PassRefPtr<EventListener> listener) { setAttributeEventListener(eventNames().eventName##Event, listener); } \ - - #define DECLARE_FORWARDING_ATTRIBUTE_EVENT_LISTENER(recipient, attribute) \ - EventListener* on##attribute(); \ - void setOn##attribute(PassRefPtr<EventListener> listener); - - #define DEFINE_FORWARDING_ATTRIBUTE_EVENT_LISTENER(type, recipient, attribute) \ - EventListener* type::on##attribute() { return recipient ? recipient->getAttributeEventListener(eventNames().attribute##Event) : 0; } \ - void type::setOn##attribute(PassRefPtr<EventListener> listener) { if (recipient) recipient->setAttributeEventListener(eventNames().attribute##Event, listener); } - - inline void EventTarget::visitJSEventListeners(JSC::SlotVisitor& visitor) - { - EventListenerIterator iterator(this); - while (EventListener* listener = iterator.nextListener()) - listener->visitJSFunction(visitor); - } - - inline bool EventTarget::isFiringEventListeners() - { - EventTargetData* d = eventTargetData(); - if (!d) - return false; - return d->firingEventIterators && !d->firingEventIterators->isEmpty(); - } - - inline bool EventTarget::hasEventListeners() const - { - EventTargetData* d = const_cast<EventTarget*>(this)->eventTargetData(); - if (!d) - return false; - return !d->eventListenerMap.isEmpty(); - } - - inline bool EventTarget::hasEventListeners(const AtomicString& eventType) - { - EventTargetData* d = eventTargetData(); - if (!d) - return false; - return d->eventListenerMap.contains(eventType); - } - - inline bool EventTarget::hasCapturingEventListeners(const AtomicString& eventType) - { - EventTargetData* d = eventTargetData(); - if (!d) - return false; - return d->eventListenerMap.containsCapturing(eventType); - } + using AddEventListenerOptionsOrBoolean = Variant<AddEventListenerOptions, bool>; + WEBCORE_EXPORT void addEventListenerForBindings(const AtomicString& eventType, RefPtr<EventListener>&&, AddEventListenerOptionsOrBoolean&&); + using ListenerOptionsOrBoolean = Variant<ListenerOptions, bool>; + WEBCORE_EXPORT void removeEventListenerForBindings(const AtomicString& eventType, RefPtr<EventListener>&&, ListenerOptionsOrBoolean&&); + WEBCORE_EXPORT ExceptionOr<bool> dispatchEventForBindings(Event&); + + virtual bool addEventListener(const AtomicString& eventType, Ref<EventListener>&&, const AddEventListenerOptions& = { }); + virtual bool removeEventListener(const AtomicString& eventType, EventListener&, const ListenerOptions&); + + virtual void removeAllEventListeners(); + virtual bool dispatchEvent(Event&); + virtual void uncaughtExceptionInEventHandler(); + + // Used for legacy "onevent" attributes. + bool setAttributeEventListener(const AtomicString& eventType, RefPtr<EventListener>&&, DOMWrapperWorld&); + EventListener* attributeEventListener(const AtomicString& eventType, DOMWrapperWorld&); + + bool hasEventListeners() const; + bool hasEventListeners(const AtomicString& eventType) const; + bool hasCapturingEventListeners(const AtomicString& eventType); + bool hasActiveEventListeners(const AtomicString& eventType) const; + const EventListenerVector& eventListeners(const AtomicString& eventType); + + bool fireEventListeners(Event&); + bool isFiringEventListeners() const; + + void visitJSEventListeners(JSC::SlotVisitor&); + void invalidateJSEventListeners(JSC::JSObject*); + +protected: + virtual ~EventTarget() = default; + + virtual EventTargetData* eventTargetData() = 0; + virtual EventTargetData* eventTargetDataConcurrently() = 0; + virtual EventTargetData& ensureEventTargetData() = 0; + const EventTargetData* eventTargetData() const; + +private: + virtual void refEventTarget() = 0; + virtual void derefEventTarget() = 0; + + void fireEventListeners(Event&, EventListenerVector); + + friend class EventListenerIterator; +}; + +class EventTargetWithInlineData : public EventTarget { +protected: + EventTargetData* eventTargetData() final { return &m_eventTargetData; } + EventTargetData* eventTargetDataConcurrently() final { return &m_eventTargetData; } + EventTargetData& ensureEventTargetData() final { return m_eventTargetData; } +private: + EventTargetData m_eventTargetData; +}; + +inline const EventTargetData* EventTarget::eventTargetData() const +{ + return const_cast<EventTarget*>(this)->eventTargetData(); +} + +inline bool EventTarget::isFiringEventListeners() const +{ + auto* data = eventTargetData(); + return data && data->isFiringEventListeners; +} + +inline bool EventTarget::hasEventListeners() const +{ + auto* data = eventTargetData(); + return data && !data->eventListenerMap.isEmpty(); +} + +inline bool EventTarget::hasEventListeners(const AtomicString& eventType) const +{ + auto* data = eventTargetData(); + return data && data->eventListenerMap.contains(eventType); +} + +inline bool EventTarget::hasCapturingEventListeners(const AtomicString& eventType) +{ + auto* data = eventTargetData(); + return data && data->eventListenerMap.containsCapturing(eventType); +} } // namespace WebCore - -#endif // EventTarget_h diff --git a/Source/WebCore/dom/EventTarget.idl b/Source/WebCore/dom/EventTarget.idl index 5d9a3e5bf..2c9384533 100644 --- a/Source/WebCore/dom/EventTarget.idl +++ b/Source/WebCore/dom/EventTarget.idl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006-2017 Apple Inc. All rights reserved. * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> * * This library is free software; you can redistribute it and/or @@ -18,21 +18,24 @@ * Boston, MA 02110-1301, USA. */ -// Introduced in DOM Level 2: [ - NoInterfaceObject, - ObjCProtocol, - CPPPureInterface, CustomToJSObject, + Exposed=(Window,Worker), + IsImmutablePrototypeExoticObjectOnPrototype, + JSCustomHeader, + JSCustomMarkFunction, JSCustomToNativeObject, - EventTarget, ] interface EventTarget { - [ObjCLegacyUnnamedParameters] void addEventListener(DOMString type, - EventListener listener, - optional boolean useCapture); - [ObjCLegacyUnnamedParameters] void removeEventListener(DOMString type, - EventListener listener, - optional boolean useCapture); - [RaisesException] boolean dispatchEvent(Event event); + [ImplementedAs=addEventListenerForBindings] void addEventListener([AtomicString] DOMString type, EventListener? callback, optional (AddEventListenerOptions or boolean) options = false); + [ImplementedAs=removeEventListenerForBindings] void removeEventListener([AtomicString] DOMString type, EventListener? callback, optional (EventListenerOptions or boolean) options = false); + [ImplementedAs=dispatchEventForBindings, MayThrowException] boolean dispatchEvent(Event event); }; +dictionary EventListenerOptions { + boolean capture = false; +}; + +dictionary AddEventListenerOptions : EventListenerOptions { + boolean passive = false; + boolean once = false; +}; diff --git a/Source/WebCore/dom/EventTargetFactory.in b/Source/WebCore/dom/EventTargetFactory.in index a75085aab..1c441ec83 100644 --- a/Source/WebCore/dom/EventTargetFactory.in +++ b/Source/WebCore/dom/EventTargetFactory.in @@ -1,43 +1,40 @@ namespace="EventTarget" +ApplePaySession conditional=APPLE_PAY AudioNode conditional=WEB_AUDIO AudioContext conditional=WEB_AUDIO AudioTrackList conditional=VIDEO_TRACK -BatteryManager conditional=BATTERY_STATUS DedicatedWorkerGlobalScope DOMApplicationCache DOMWindow EventSource -FileReader conditional=BLOB -FontLoader conditional=FONT_LOAD_EVENTS +FileReader +FontFaceSet IDBDatabase conditional=INDEXED_DATABASE IDBOpenDBRequest conditional=INDEXED_DATABASE IDBRequest conditional=INDEXED_DATABASE IDBTransaction conditional=INDEXED_DATABASE -MediaKeySession conditional=ENCRYPTED_MEDIA_V2 MediaController conditional=VIDEO +MediaKeySession conditional=ENCRYPTED_MEDIA +MediaRemoteControls conditional=MEDIA_SESSION MediaSource conditional=MEDIA_SOURCE MediaStream conditional=MEDIA_STREAM MediaStreamTrack conditional=MEDIA_STREAM MessagePort -NetworkInfoConnection conditional=NETWORK_INFO Node Notification conditional=NOTIFICATIONS|LEGACY_NOTIFICATIONS Performance conditional=WEB_TIMING -RTCDataChannel conditional=MEDIA_STREAM -RTCDTMFSender conditional=MEDIA_STREAM -RTCPeerConnection conditional=MEDIA_STREAM -SharedWorker conditional=SHARED_WORKERS -SharedWorkerGlobalScope conditional=SHARED_WORKERS +RTCDataChannel conditional=WEB_RTC +RTCDTMFSender conditional=WEB_RTC +RTCPeerConnection conditional=WEB_RTC SourceBuffer conditional=MEDIA_SOURCE SourceBufferList conditional=MEDIA_SOURCE -SpeechRecognition conditional=SCRIPTED_SPEECH SpeechSynthesisUtterance conditional=SPEECH_SYNTHESIS -SVGElementInstance conditional=SVG TextTrack conditional=VIDEO_TRACK TextTrackCue conditional=VIDEO_TRACK TextTrackList conditional=VIDEO_TRACK VideoTrackList conditional=VIDEO_TRACK +WebKitMediaKeySession conditional=LEGACY_ENCRYPTED_MEDIA WebKitNamedFlow WebSocket conditional=WEB_SOCKETS Worker diff --git a/Source/WebCore/dom/Exception.h b/Source/WebCore/dom/Exception.h new file mode 100644 index 000000000..4af0d1e46 --- /dev/null +++ b/Source/WebCore/dom/Exception.h @@ -0,0 +1,62 @@ +/* + +Copyright (C) 2016 Apple Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#pragma once + +#include <wtf/text/WTFString.h> + +namespace WebCore { + +using ExceptionCode = int; + +class Exception { +public: + explicit Exception(ExceptionCode, String&& = { }); + + ExceptionCode code() const { return m_code; } + const String& message() const { return m_message; } + String&& releaseMessage() { return WTFMove(m_message); } + +private: + ExceptionCode m_code; + String m_message; +}; + +Exception isolatedCopy(Exception&&); + +inline Exception::Exception(ExceptionCode code, String&& message) + : m_code(code) + , m_message(WTFMove(message)) +{ + ASSERT(code); +} + +inline Exception isolatedCopy(Exception&& value) +{ + return Exception { value.code(), value.releaseMessage().isolatedCopy() }; +} + +} diff --git a/Source/WebCore/dom/ExceptionBase.cpp b/Source/WebCore/dom/ExceptionBase.cpp index 2ea679b1e..b6a44d0e0 100644 --- a/Source/WebCore/dom/ExceptionBase.cpp +++ b/Source/WebCore/dom/ExceptionBase.cpp @@ -10,7 +10,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -29,23 +29,41 @@ #include "config.h" #include "ExceptionBase.h" +#include "ExceptionCodeDescription.h" namespace WebCore { ExceptionBase::ExceptionBase(const ExceptionCodeDescription& description) : m_code(description.code) , m_name(description.name) - , m_description(description.description) + , m_message(description.description) + , m_typeName(description.typeName) +{ +} + +ExceptionBase::ExceptionBase(unsigned short code, const String& name, const String& message, const String& typeName) + : m_code(code) + , m_name(name) + , m_message(message) + , m_typeName(typeName) { - if (description.name) - m_message = m_name + ": " + description.typeName + " Exception " + String::number(description.code); - else - m_message = makeString(description.typeName, " Exception ", String::number(description.code)); } String ExceptionBase::toString() const { - return "Error: " + m_message; + if (!m_toString.isEmpty()) + return m_toString; + + String lastComponent; + if (!m_message.isEmpty()) + lastComponent = makeString(": ", m_message); + + if (m_name.isEmpty()) + m_toString = makeString(m_typeName, " Exception", m_code ? makeString(" ", String::number(m_code)) : "", lastComponent); + else + m_toString = makeString(m_name, " (", m_typeName, " Exception", m_code ? makeString(" ", String::number(m_code)) : "", ")", lastComponent); + + return m_toString; } } // namespace WebCore diff --git a/Source/WebCore/dom/ExceptionBase.h b/Source/WebCore/dom/ExceptionBase.h index 01e5f1d9d..d50550775 100644 --- a/Source/WebCore/dom/ExceptionBase.h +++ b/Source/WebCore/dom/ExceptionBase.h @@ -10,7 +10,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -26,36 +26,34 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ExceptionBase_h -#define ExceptionBase_h +#pragma once -#include "ExceptionCode.h" -#include <wtf/RefCounted.h> #include <wtf/text/WTFString.h> namespace WebCore { struct ExceptionCodeDescription; +using ExceptionCode = int; + class ExceptionBase : public RefCounted<ExceptionBase> { public: unsigned short code() const { return m_code; } String name() const { return m_name; } String message() const { return m_message; } - String description() const { return m_description; } String toString() const; protected: explicit ExceptionBase(const ExceptionCodeDescription&); + ExceptionBase(unsigned short code, const String& name, const String& message, const String& typeName); private: unsigned short m_code; String m_name; String m_message; - String m_description; + String m_typeName; + mutable String m_toString; }; } // namespace WebCore - -#endif // ExceptionBase_h diff --git a/Source/WebCore/dom/ExceptionCode.h b/Source/WebCore/dom/ExceptionCode.h index 88f5f10b3..496a73653 100644 --- a/Source/WebCore/dom/ExceptionCode.h +++ b/Source/WebCore/dom/ExceptionCode.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2015 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -16,60 +16,72 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef ExceptionCode_h -#define ExceptionCode_h - -// FIXME: Move this header into the files that actually need it. -#include "ExceptionCodeDescription.h" +#pragma once namespace WebCore { - // The DOM standards use unsigned short for exception codes. - // In our DOM implementation we use int instead, and use different - // numerical ranges for different types of DOM exception, so that - // an exception of any type can be expressed with a single integer. - typedef int ExceptionCode; +// The DOM standards use unsigned short for exception codes. +// In our DOM implementation we use int instead, and use different +// numerical ranges for different types of DOM exception, so that +// an exception of any type can be expressed with a single integer. +using ExceptionCode = int; +// Some of these are considered historical since they have been +// changed or removed from the specifications. +enum { + INDEX_SIZE_ERR = 1, + HIERARCHY_REQUEST_ERR = 3, + WRONG_DOCUMENT_ERR = 4, + INVALID_CHARACTER_ERR = 5, + NO_MODIFICATION_ALLOWED_ERR = 7, + NOT_FOUND_ERR = 8, + NOT_SUPPORTED_ERR = 9, + INUSE_ATTRIBUTE_ERR = 10, // Historical. Only used in setAttributeNode etc which have been removed from the DOM specs. - // Some of these are considered historical since they have been - // changed or removed from the specifications. - enum { - INDEX_SIZE_ERR = 1, - HIERARCHY_REQUEST_ERR = 3, - WRONG_DOCUMENT_ERR = 4, - INVALID_CHARACTER_ERR = 5, - NO_MODIFICATION_ALLOWED_ERR = 7, - NOT_FOUND_ERR = 8, - NOT_SUPPORTED_ERR = 9, - INUSE_ATTRIBUTE_ERR = 10, // Historical. Only used in setAttributeNode etc which have been removed from the DOM specs. + // Introduced in DOM Level 2: + INVALID_STATE_ERR = 11, + SYNTAX_ERR = 12, + INVALID_MODIFICATION_ERR = 13, + NAMESPACE_ERR = 14, + INVALID_ACCESS_ERR = 15, - // Introduced in DOM Level 2: - INVALID_STATE_ERR = 11, - SYNTAX_ERR = 12, - INVALID_MODIFICATION_ERR = 13, - NAMESPACE_ERR = 14, - INVALID_ACCESS_ERR = 15, + // Introduced in DOM Level 3: + TYPE_MISMATCH_ERR = 17, // Historical; use TypeError instead - // Introduced in DOM Level 3: - TYPE_MISMATCH_ERR = 17, // Historical; use TypeError instead + // XMLHttpRequest extension: + SECURITY_ERR = 18, - // XMLHttpRequest extension: - SECURITY_ERR = 18, + // Others introduced in HTML5: + NETWORK_ERR = 19, + ABORT_ERR = 20, + URL_MISMATCH_ERR = 21, + QUOTA_EXCEEDED_ERR = 22, + TIMEOUT_ERR = 23, + INVALID_NODE_TYPE_ERR = 24, + DATA_CLONE_ERR = 25, - // Others introduced in HTML5: - NETWORK_ERR = 19, - ABORT_ERR = 20, - URL_MISMATCH_ERR = 21, - QUOTA_EXCEEDED_ERR = 22, - TIMEOUT_ERR = 23, - INVALID_NODE_TYPE_ERR = 24, - DATA_CLONE_ERR = 25, + // Others introduced in https://heycam.github.io/webidl/#idl-exceptions + EncodingError, + NotReadableError, + UnknownError, + ConstraintError, + DataError, + TransactionInactiveError, + ReadonlyError, + VersionError, + OperationError, + NotAllowedError, - // WebIDL exception types, handled by the binding layer. - // FIXME: Add GeneralError, EvalError, etc. when implemented in the bindings. - TypeError = 105, - }; + // Non-standard errors + StackOverflowError, -} // namespace WebCore + // Used to indicate to the bindings that a JS exception was thrown below and it should be propogated. + ExistingExceptionError, -#endif // ExceptionCode_h + // WebIDL exception types, handled by the binding layer. + // FIXME: Add GeneralError, EvalError, etc. when implemented in the bindings. + TypeError = 105, + RangeError = 106, +}; + +} // namespace WebCore diff --git a/Source/WebCore/dom/ExceptionCodePlaceholder.h b/Source/WebCore/dom/ExceptionCodePlaceholder.h deleted file mode 100644 index aadd5279d..000000000 --- a/Source/WebCore/dom/ExceptionCodePlaceholder.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef ExceptionCodePlaceholder_h -#define ExceptionCodePlaceholder_h - -#include <wtf/Assertions.h> -#include <wtf/Noncopyable.h> - -namespace WebCore { - -typedef int ExceptionCode; - -class ExceptionCodePlaceholder { - WTF_MAKE_NONCOPYABLE(ExceptionCodePlaceholder); -public: - ExceptionCodePlaceholder() { } - explicit ExceptionCodePlaceholder(ExceptionCode); - - operator ExceptionCode& () const { return m_code; } - -protected: - mutable ExceptionCode m_code; -}; - -inline ExceptionCodePlaceholder::ExceptionCodePlaceholder(ExceptionCode code) - : m_code(code) -{ -} - -class IgnorableExceptionCode : public ExceptionCodePlaceholder { -}; - -#define IGNORE_EXCEPTION ::WebCore::IgnorableExceptionCode() - -#if ASSERT_DISABLED - -#define ASSERT_NO_EXCEPTION ::WebCore::IgnorableExceptionCode() - -#else - -class NoExceptionAssertionChecker : public ExceptionCodePlaceholder { -public: - NoExceptionAssertionChecker(const char* file, int line); - ~NoExceptionAssertionChecker(); - -private: - static const ExceptionCode defaultExceptionCode = 0xaaaaaaaa; - const char* m_file; - int m_line; -}; - -#define ASSERT_NO_EXCEPTION ::WebCore::NoExceptionAssertionChecker(__FILE__, __LINE__) - -#endif - -} - -#endif diff --git a/Source/WebCore/dom/ExceptionOr.h b/Source/WebCore/dom/ExceptionOr.h new file mode 100644 index 000000000..7455777b9 --- /dev/null +++ b/Source/WebCore/dom/ExceptionOr.h @@ -0,0 +1,170 @@ +/* + +Copyright (C) 2016 Apple Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#pragma once + +#include "Exception.h" +#include <wtf/Expected.h> + +namespace WebCore { + +template<typename ReturnType> class ExceptionOr { +public: + ExceptionOr(Exception&&); + ExceptionOr(ReturnType&&); + template<typename OtherType> ExceptionOr(const OtherType&, typename std::enable_if<std::is_scalar<OtherType>::value && std::is_convertible<OtherType, ReturnType>::value>::type* = nullptr); + + bool hasException() const; + const Exception& exception() const; + Exception&& releaseException(); + ReturnType&& releaseReturnValue(); + +private: + Expected<ReturnType, Exception> m_value; +}; + +template<typename ReturnReferenceType> class ExceptionOr<ReturnReferenceType&> { +public: + ExceptionOr(Exception&&); + ExceptionOr(ReturnReferenceType&); + + bool hasException() const; + const Exception& exception() const; + Exception&& releaseException(); + ReturnReferenceType& releaseReturnValue(); + +private: + ExceptionOr<ReturnReferenceType*> m_value; +}; + +template<> class ExceptionOr<void> { +public: + ExceptionOr(Exception&&); + ExceptionOr() = default; + + bool hasException() const; + const Exception& exception() const; + Exception&& releaseException(); + +private: + Expected<void, Exception> m_value; +}; + +ExceptionOr<void> isolatedCopy(ExceptionOr<void>&&); + +template<typename ReturnType> inline ExceptionOr<ReturnType>::ExceptionOr(Exception&& exception) + : m_value(makeUnexpected(WTFMove(exception))) +{ +} + +template<typename ReturnType> inline ExceptionOr<ReturnType>::ExceptionOr(ReturnType&& returnValue) + : m_value(WTFMove(returnValue)) +{ +} + +template<typename ReturnType> template<typename OtherType> inline ExceptionOr<ReturnType>::ExceptionOr(const OtherType& returnValue, typename std::enable_if<std::is_scalar<OtherType>::value && std::is_convertible<OtherType, ReturnType>::value>::type*) + : m_value(static_cast<ReturnType>(returnValue)) +{ +} + +template<typename ReturnType> inline bool ExceptionOr<ReturnType>::hasException() const +{ + return !m_value.hasValue(); +} + +template<typename ReturnType> inline Exception&& ExceptionOr<ReturnType>::releaseException() +{ + return WTFMove(m_value.error()); +} + +template<typename ReturnType> inline const Exception& ExceptionOr<ReturnType>::exception() const +{ + return m_value.error(); +} + +template<typename ReturnType> inline ReturnType&& ExceptionOr<ReturnType>::releaseReturnValue() +{ + return WTFMove(m_value.value()); +} + +template<typename ReturnReferenceType> inline ExceptionOr<ReturnReferenceType&>::ExceptionOr(Exception&& exception) + : m_value(WTFMove(exception)) +{ +} + +template<typename ReturnReferenceType> inline ExceptionOr<ReturnReferenceType&>::ExceptionOr(ReturnReferenceType& returnValue) + : m_value(&returnValue) +{ +} + +template<typename ReturnReferenceType> inline bool ExceptionOr<ReturnReferenceType&>::hasException() const +{ + return m_value.hasException(); +} + +template<typename ReturnReferenceType> inline const Exception& ExceptionOr<ReturnReferenceType&>::exception() const +{ + return m_value.exception(); +} + +template<typename ReturnReferenceType> inline Exception&& ExceptionOr<ReturnReferenceType&>::releaseException() +{ + return m_value.releaseException(); +} + +template<typename ReturnReferenceType> inline ReturnReferenceType& ExceptionOr<ReturnReferenceType&>::releaseReturnValue() +{ + return *m_value.releaseReturnValue(); +} + +inline ExceptionOr<void>::ExceptionOr(Exception&& exception) + : m_value(makeUnexpected(WTFMove(exception))) +{ +} + +inline bool ExceptionOr<void>::hasException() const +{ + return !m_value.hasValue(); +} + +inline const Exception& ExceptionOr<void>::exception() const +{ + return m_value.error(); +} + +inline Exception&& ExceptionOr<void>::releaseException() +{ + return WTFMove(m_value.error()); +} + +inline ExceptionOr<void> isolatedCopy(ExceptionOr<void>&& value) +{ + if (value.hasException()) + return isolatedCopy(value.releaseException()); + return { }; +} + +} diff --git a/Source/WebCore/dom/ExtensionStyleSheets.cpp b/Source/WebCore/dom/ExtensionStyleSheets.cpp new file mode 100644 index 000000000..d0d244af3 --- /dev/null +++ b/Source/WebCore/dom/ExtensionStyleSheets.cpp @@ -0,0 +1,213 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * (C) 2006 Alexey Proskuryakov (ap@webkit.org) + * Copyright (C) 2004-2009, 2011-2012, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * Copyright (C) 2008, 2009, 2011, 2012 Google Inc. All rights reserved. + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) Research In Motion Limited 2010-2011. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "ExtensionStyleSheets.h" + +#include "CSSStyleSheet.h" +#include "Element.h" +#include "HTMLLinkElement.h" +#include "HTMLStyleElement.h" +#include "Page.h" +#include "ProcessingInstruction.h" +#include "SVGStyleElement.h" +#include "Settings.h" +#include "StyleInvalidationAnalysis.h" +#include "StyleResolver.h" +#include "StyleScope.h" +#include "StyleSheetContents.h" +#include "StyleSheetList.h" +#include "UserContentController.h" +#include "UserContentURLPattern.h" +#include "UserStyleSheet.h" + +namespace WebCore { + +using namespace ContentExtensions; +using namespace HTMLNames; + +ExtensionStyleSheets::ExtensionStyleSheets(Document& document) + : m_document(document) +{ +} + +static Ref<CSSStyleSheet> createExtensionsStyleSheet(Document& document, URL url, const String& text, UserStyleLevel level) +{ + auto contents = StyleSheetContents::create(url, CSSParserContext(document, url)); + auto styleSheet = CSSStyleSheet::create(contents.get(), document, true); + + contents->setIsUserStyleSheet(level == UserStyleUserLevel); + contents->parseString(text); + + return styleSheet; +} + +CSSStyleSheet* ExtensionStyleSheets::pageUserSheet() +{ + if (m_pageUserSheet) + return m_pageUserSheet.get(); + + Page* owningPage = m_document.page(); + if (!owningPage) + return 0; + + String userSheetText = owningPage->userStyleSheet(); + if (userSheetText.isEmpty()) + return 0; + + m_pageUserSheet = createExtensionsStyleSheet(m_document, m_document.settings().userStyleSheetLocation(), userSheetText, UserStyleUserLevel); + + return m_pageUserSheet.get(); +} + +void ExtensionStyleSheets::clearPageUserSheet() +{ + if (m_pageUserSheet) { + m_pageUserSheet = nullptr; + m_document.styleScope().didChangeStyleSheetEnvironment(); + } +} + +void ExtensionStyleSheets::updatePageUserSheet() +{ + clearPageUserSheet(); + if (pageUserSheet()) + m_document.styleScope().didChangeStyleSheetEnvironment(); +} + +const Vector<RefPtr<CSSStyleSheet>>& ExtensionStyleSheets::injectedUserStyleSheets() const +{ + updateInjectedStyleSheetCache(); + return m_injectedUserStyleSheets; +} + +const Vector<RefPtr<CSSStyleSheet>>& ExtensionStyleSheets::injectedAuthorStyleSheets() const +{ + updateInjectedStyleSheetCache(); + return m_injectedAuthorStyleSheets; +} + +void ExtensionStyleSheets::updateInjectedStyleSheetCache() const +{ + if (m_injectedStyleSheetCacheValid) + return; + m_injectedStyleSheetCacheValid = true; + m_injectedUserStyleSheets.clear(); + m_injectedAuthorStyleSheets.clear(); + + Page* owningPage = m_document.page(); + if (!owningPage) + return; + + owningPage->userContentProvider().forEachUserStyleSheet([&](const UserStyleSheet& userStyleSheet) { + if (userStyleSheet.injectedFrames() == InjectInTopFrameOnly && m_document.ownerElement()) + return; + + if (!UserContentURLPattern::matchesPatterns(m_document.url(), userStyleSheet.whitelist(), userStyleSheet.blacklist())) + return; + + auto sheet = createExtensionsStyleSheet(const_cast<Document&>(m_document), userStyleSheet.url(), userStyleSheet.source(), userStyleSheet.level()); + + if (userStyleSheet.level() == UserStyleUserLevel) + m_injectedUserStyleSheets.append(WTFMove(sheet)); + else + m_injectedAuthorStyleSheets.append(WTFMove(sheet)); + }); + + if (!owningPage->captionUserPreferencesStyleSheet().isEmpty()) { + // Identify our override style sheet with a unique URL - a new scheme and a UUID. + static NeverDestroyed<URL> captionsStyleSheetURL(ParsedURLString, "user-captions-override:01F6AF12-C3B0-4F70-AF5E-A3E00234DC23"); + + auto sheet = createExtensionsStyleSheet(const_cast<Document&>(m_document), captionsStyleSheetURL, owningPage->captionUserPreferencesStyleSheet(), UserStyleAuthorLevel); + + m_injectedAuthorStyleSheets.append(WTFMove(sheet)); + } +} + +void ExtensionStyleSheets::invalidateInjectedStyleSheetCache() +{ + m_injectedStyleSheetCacheValid = false; + m_document.styleScope().didChangeStyleSheetEnvironment(); +} + +void ExtensionStyleSheets::addUserStyleSheet(Ref<StyleSheetContents>&& userSheet) +{ + ASSERT(userSheet.get().isUserStyleSheet()); + m_userStyleSheets.append(CSSStyleSheet::create(WTFMove(userSheet), m_document)); + m_document.styleScope().didChangeStyleSheetEnvironment(); +} + +void ExtensionStyleSheets::addAuthorStyleSheetForTesting(Ref<StyleSheetContents>&& authorSheet) +{ + ASSERT(!authorSheet.get().isUserStyleSheet()); + m_authorStyleSheetsForTesting.append(CSSStyleSheet::create(WTFMove(authorSheet), m_document)); + m_document.styleScope().didChangeStyleSheetEnvironment(); +} + +#if ENABLE(CONTENT_EXTENSIONS) +void ExtensionStyleSheets::addDisplayNoneSelector(const String& identifier, const String& selector, uint32_t selectorID) +{ + auto result = m_contentExtensionSelectorSheets.add(identifier, nullptr); + if (result.isNewEntry) { + result.iterator->value = ContentExtensionStyleSheet::create(m_document); + m_userStyleSheets.append(&result.iterator->value->styleSheet()); + } + + if (result.iterator->value->addDisplayNoneSelector(selector, selectorID)) + m_document.styleScope().didChangeStyleSheetEnvironment(); +} + +void ExtensionStyleSheets::maybeAddContentExtensionSheet(const String& identifier, StyleSheetContents& sheet) +{ + ASSERT(sheet.isUserStyleSheet()); + + if (m_contentExtensionSheets.contains(identifier)) + return; + + Ref<CSSStyleSheet> cssSheet = CSSStyleSheet::create(sheet, m_document); + m_contentExtensionSheets.set(identifier, &cssSheet.get()); + m_userStyleSheets.append(adoptRef(cssSheet.leakRef())); + m_document.styleScope().didChangeStyleSheetEnvironment(); + +} +#endif // ENABLE(CONTENT_EXTENSIONS) + +void ExtensionStyleSheets::detachFromDocument() +{ + if (m_pageUserSheet) + m_pageUserSheet->detachFromDocument(); + for (auto& sheet : m_injectedUserStyleSheets) + sheet->detachFromDocument(); + for (auto& sheet : m_injectedAuthorStyleSheets) + sheet->detachFromDocument(); + for (auto& sheet : m_userStyleSheets) + sheet->detachFromDocument(); + for (auto& sheet : m_authorStyleSheetsForTesting) + sheet->detachFromDocument(); +} + +} diff --git a/Source/WebCore/dom/ExtensionStyleSheets.h b/Source/WebCore/dom/ExtensionStyleSheets.h new file mode 100644 index 000000000..6be6abd1e --- /dev/null +++ b/Source/WebCore/dom/ExtensionStyleSheets.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * (C) 2006 Alexey Proskuryakov (ap@webkit.org) + * Copyright (C) 2004-2010, 2012-2013, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#pragma once + +#include "Timer.h" +#include <memory> +#include <wtf/FastMalloc.h> +#include <wtf/HashMap.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> +#include <wtf/text/WTFString.h> + +#if ENABLE(CONTENT_EXTENSIONS) +#include "ContentExtensionStyleSheet.h" +#endif + +namespace WebCore { + +class CSSStyleSheet; +class Document; +class Node; +class StyleSheet; +class StyleSheetContents; +class StyleSheetList; + +class ExtensionStyleSheets { + WTF_MAKE_FAST_ALLOCATED; +public: + explicit ExtensionStyleSheets(Document&); + + CSSStyleSheet* pageUserSheet(); + const Vector<RefPtr<CSSStyleSheet>>& documentUserStyleSheets() const { return m_userStyleSheets; } + const Vector<RefPtr<CSSStyleSheet>>& injectedUserStyleSheets() const; + const Vector<RefPtr<CSSStyleSheet>>& injectedAuthorStyleSheets() const; + const Vector<RefPtr<CSSStyleSheet>>& authorStyleSheetsForTesting() const { return m_authorStyleSheetsForTesting; } + + void clearPageUserSheet(); + void updatePageUserSheet(); + void invalidateInjectedStyleSheetCache(); + void updateInjectedStyleSheetCache() const; + + WEBCORE_EXPORT void addUserStyleSheet(Ref<StyleSheetContents>&&); + + WEBCORE_EXPORT void addAuthorStyleSheetForTesting(Ref<StyleSheetContents>&&); + +#if ENABLE(CONTENT_EXTENSIONS) + void addDisplayNoneSelector(const String& identifier, const String& selector, uint32_t selectorID); + void maybeAddContentExtensionSheet(const String& identifier, StyleSheetContents&); +#endif + + void detachFromDocument(); + +private: + Document& m_document; + + RefPtr<CSSStyleSheet> m_pageUserSheet; + + mutable Vector<RefPtr<CSSStyleSheet>> m_injectedUserStyleSheets; + mutable Vector<RefPtr<CSSStyleSheet>> m_injectedAuthorStyleSheets; + mutable bool m_injectedStyleSheetCacheValid { false }; + + Vector<RefPtr<CSSStyleSheet>> m_userStyleSheets; + Vector<RefPtr<CSSStyleSheet>> m_authorStyleSheetsForTesting; + +#if ENABLE(CONTENT_EXTENSIONS) + HashMap<String, RefPtr<CSSStyleSheet>> m_contentExtensionSheets; + HashMap<String, RefPtr<ContentExtensions::ContentExtensionStyleSheet>> m_contentExtensionSelectorSheets; +#endif +}; + +} // namespace WebCore diff --git a/Source/WebCore/dom/FocusEvent.cpp b/Source/WebCore/dom/FocusEvent.cpp index d1aaa1e98..16d8b3ff0 100644 --- a/Source/WebCore/dom/FocusEvent.cpp +++ b/Source/WebCore/dom/FocusEvent.cpp @@ -30,11 +30,6 @@ namespace WebCore { -FocusEventInit::FocusEventInit() - : relatedTarget(0) -{ -} - EventInterface FocusEvent::eventInterface() const { return FocusEventInterfaceType; @@ -45,18 +40,14 @@ bool FocusEvent::isFocusEvent() const return true; } -FocusEvent::FocusEvent() -{ -} - -FocusEvent::FocusEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<AbstractView> view, int detail, PassRefPtr<EventTarget> relatedTarget) +FocusEvent::FocusEvent(const AtomicString& type, bool canBubble, bool cancelable, DOMWindow* view, int detail, RefPtr<EventTarget>&& relatedTarget) : UIEvent(type, canBubble, cancelable, view, detail) - , m_relatedTarget(relatedTarget) + , m_relatedTarget(WTFMove(relatedTarget)) { } -FocusEvent::FocusEvent(const AtomicString& type, const FocusEventInit& initializer) - : UIEvent(type, initializer) +FocusEvent::FocusEvent(const AtomicString& type, const Init& initializer, IsTrusted isTrusted) + : UIEvent(type, initializer, isTrusted) , m_relatedTarget(initializer.relatedTarget) { } diff --git a/Source/WebCore/dom/FocusEvent.h b/Source/WebCore/dom/FocusEvent.h index 474f45db9..7733173cf 100644 --- a/Source/WebCore/dom/FocusEvent.h +++ b/Source/WebCore/dom/FocusEvent.h @@ -23,8 +23,7 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef FocusEvent_h -#define FocusEvent_h +#pragma once #include "EventTarget.h" #include "UIEvent.h" @@ -33,55 +32,36 @@ namespace WebCore { class Node; -struct FocusEventInit : public UIEventInit { - FocusEventInit(); - - RefPtr<EventTarget> relatedTarget; -}; - -class FocusEvent : public UIEvent { +class FocusEvent final : public UIEvent { public: - static PassRefPtr<FocusEvent> create() + static Ref<FocusEvent> create(const AtomicString& type, bool canBubble, bool cancelable, DOMWindow* view, int detail, RefPtr<EventTarget>&& relatedTarget) { - return adoptRef(new FocusEvent); + return adoptRef(*new FocusEvent(type, canBubble, cancelable, view, detail, WTFMove(relatedTarget))); } - static PassRefPtr<FocusEvent> create(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<AbstractView> view, int detail, PassRefPtr<EventTarget> relatedTarget) - { - return adoptRef(new FocusEvent(type, canBubble, cancelable, view, detail, relatedTarget)); - } + struct Init : UIEventInit { + RefPtr<EventTarget> relatedTarget; + }; - static PassRefPtr<FocusEvent> create(const AtomicString& type, const FocusEventInit& initializer) + static Ref<FocusEvent> create(const AtomicString& type, const Init& initializer, IsTrusted isTrusted = IsTrusted::No) { - return adoptRef(new FocusEvent(type, initializer)); + return adoptRef(*new FocusEvent(type, initializer, isTrusted)); } - virtual EventTarget* relatedTarget() const override final { return m_relatedTarget.get(); } - void setRelatedTarget(PassRefPtr<EventTarget> relatedTarget) { m_relatedTarget = relatedTarget; } + EventTarget* relatedTarget() const override { return m_relatedTarget.get(); } + void setRelatedTarget(RefPtr<EventTarget>&& relatedTarget) { m_relatedTarget = WTFMove(relatedTarget); } - virtual EventInterface eventInterface() const override; - virtual bool isFocusEvent() const override; + EventInterface eventInterface() const override; private: - FocusEvent(); - FocusEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<AbstractView>, int, PassRefPtr<EventTarget>); - FocusEvent(const AtomicString& type, const FocusEventInit&); + FocusEvent(const AtomicString& type, bool canBubble, bool cancelable, DOMWindow*, int, RefPtr<EventTarget>&&); + FocusEvent(const AtomicString& type, const Init&, IsTrusted); + + bool isFocusEvent() const override; RefPtr<EventTarget> m_relatedTarget; }; -inline FocusEvent* toFocusEvent(Event* event) -{ - ASSERT_WITH_SECURITY_IMPLICATION(event && event->isFocusEvent()); - return static_cast<FocusEvent*>(event); -} - -inline FocusEvent& toFocusEvent(Event& event) -{ - ASSERT(event.isFocusEvent()); - return static_cast<FocusEvent&>(event); -} - } // namespace WebCore -#endif // FocusEvent_h +SPECIALIZE_TYPE_TRAITS_EVENT(FocusEvent) diff --git a/Source/WebCore/dom/FocusEvent.idl b/Source/WebCore/dom/FocusEvent.idl index fc422c3f9..48a68f003 100644 --- a/Source/WebCore/dom/FocusEvent.idl +++ b/Source/WebCore/dom/FocusEvent.idl @@ -24,8 +24,11 @@ */ [ - ConstructorConditional=DOM4_EVENTS_CONSTRUCTOR, - ConstructorTemplate=Event + Constructor(DOMString type, optional FocusEventInit eventInitDict), ] interface FocusEvent : UIEvent { - [InitializedByEventConstructor] readonly attribute EventTarget relatedTarget; + readonly attribute EventTarget? relatedTarget; +}; + +dictionary FocusEventInit : UIEventInit { + EventTarget? relatedTarget = null; }; diff --git a/Source/WebCore/dom/FragmentScriptingPermission.h b/Source/WebCore/dom/FragmentScriptingPermission.h index 21b355276..936278b7e 100644 --- a/Source/WebCore/dom/FragmentScriptingPermission.h +++ b/Source/WebCore/dom/FragmentScriptingPermission.h @@ -25,8 +25,7 @@ // FIXME: Move this file to ParserContentPolicy.h. -#ifndef ParserContentPolicy_h -#define ParserContentPolicy_h +#pragma once namespace WebCore { @@ -62,5 +61,3 @@ static inline ParserContentPolicy allowPluginContent(ParserContentPolicy parserC } } // namespace WebCore - -#endif // ParserContentPolicy_h diff --git a/Source/WebCore/dom/GenericEventQueue.cpp b/Source/WebCore/dom/GenericEventQueue.cpp index f27885642..7f56f45fa 100644 --- a/Source/WebCore/dom/GenericEventQueue.cpp +++ b/Source/WebCore/dom/GenericEventQueue.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -28,12 +28,14 @@ #include "Event.h" #include "EventTarget.h" +#include "ScriptExecutionContext.h" +#include "Timer.h" +#include <wtf/MainThread.h> namespace WebCore { GenericEventQueue::GenericEventQueue(EventTarget& owner) : m_owner(owner) - , m_timer(this, &GenericEventQueue::timerFired) , m_isClosed(false) { } @@ -42,54 +44,70 @@ GenericEventQueue::~GenericEventQueue() { } -bool GenericEventQueue::enqueueEvent(PassRefPtr<Event> event) +void GenericEventQueue::enqueueEvent(RefPtr<Event>&& event) { if (m_isClosed) - return false; + return; if (event->target() == &m_owner) - event->setTarget(0); + event->setTarget(nullptr); - m_pendingEvents.append(event); + m_pendingEvents.append(WTFMove(event)); - if (!m_timer.isActive()) - m_timer.startOneShot(0); + if (m_isSuspended) + return; - return true; + m_taskQueue.enqueueTask(std::bind(&GenericEventQueue::dispatchOneEvent, this)); } -void GenericEventQueue::timerFired(Timer<GenericEventQueue>&) +void GenericEventQueue::dispatchOneEvent() { - ASSERT(!m_timer.isActive()); ASSERT(!m_pendingEvents.isEmpty()); - Vector<RefPtr<Event>> pendingEvents; - m_pendingEvents.swap(pendingEvents); - Ref<EventTarget> protect(m_owner); - for (unsigned i = 0; i < pendingEvents.size(); ++i) { - EventTarget& target = pendingEvents[i]->target() ? *pendingEvents[i]->target() : m_owner; - target.dispatchEvent(pendingEvents[i].release()); - } + RefPtr<Event> event = m_pendingEvents.takeFirst(); + EventTarget& target = event->target() ? *event->target() : m_owner; + target.dispatchEvent(*event); } void GenericEventQueue::close() { m_isClosed = true; - m_timer.stop(); + m_taskQueue.close(); m_pendingEvents.clear(); } void GenericEventQueue::cancelAllEvents() { - m_timer.stop(); + m_taskQueue.cancelAllTasks(); m_pendingEvents.clear(); } bool GenericEventQueue::hasPendingEvents() const { - return m_pendingEvents.size(); + return !m_pendingEvents.isEmpty(); +} + +void GenericEventQueue::suspend() +{ + ASSERT(!m_isSuspended); + m_isSuspended = true; + m_taskQueue.cancelAllTasks(); +} + +void GenericEventQueue::resume() +{ + if (!m_isSuspended) + return; + + m_isSuspended = false; + + if (m_pendingEvents.isEmpty()) + return; + + for (unsigned i = 0; i < m_pendingEvents.size(); ++i) + m_taskQueue.enqueueTask(std::bind(&GenericEventQueue::dispatchOneEvent, this)); } } diff --git a/Source/WebCore/dom/GenericEventQueue.h b/Source/WebCore/dom/GenericEventQueue.h index ea68a5a32..45b9e8203 100644 --- a/Source/WebCore/dom/GenericEventQueue.h +++ b/Source/WebCore/dom/GenericEventQueue.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -23,39 +23,42 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef GenericEventQueue_h -#define GenericEventQueue_h +#pragma once -#include "Timer.h" +#include "GenericTaskQueue.h" +#include <wtf/Deque.h> #include <wtf/Forward.h> #include <wtf/RefPtr.h> -#include <wtf/Vector.h> +#include <wtf/WeakPtr.h> namespace WebCore { class Event; class EventTarget; +class Timer; class GenericEventQueue { public: explicit GenericEventQueue(EventTarget&); ~GenericEventQueue(); - bool enqueueEvent(PassRefPtr<Event>); + void enqueueEvent(RefPtr<Event>&&); void close(); void cancelAllEvents(); bool hasPendingEvents() const; + void suspend(); + void resume(); + private: - void timerFired(Timer<GenericEventQueue>&); + void dispatchOneEvent(); EventTarget& m_owner; - Vector<RefPtr<Event>> m_pendingEvents; - Timer<GenericEventQueue> m_timer; + GenericTaskQueue<Timer> m_taskQueue; + Deque<RefPtr<Event>> m_pendingEvents; bool m_isClosed; + bool m_isSuspended { false }; }; -} - -#endif +} // namespace WebCore diff --git a/Source/WebCore/dom/GlobalEventHandlers.idl b/Source/WebCore/dom/GlobalEventHandlers.idl new file mode 100644 index 000000000..01849d10c --- /dev/null +++ b/Source/WebCore/dom/GlobalEventHandlers.idl @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2015 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +// https://html.spec.whatwg.org/multipage/webappapis.html#globaleventhandlers +[ + NoInterfaceObject, +] interface GlobalEventHandlers { + // Commented out event handlers are defined by the HTML5 specification but + // are not yet implemented. + + attribute EventHandler onabort; + [Conditional=REQUEST_AUTOCOMPLETE] attribute EventHandler onautocomplete; + [Conditional=REQUEST_AUTOCOMPLETE] attribute EventHandler onautocompleteerror; + attribute EventHandler onblur; + // attribute EventHandler oncancel; + attribute EventHandler oncanplay; + attribute EventHandler oncanplaythrough; + attribute EventHandler onchange; + attribute EventHandler onclick; + // attribute EventHandler onclose; + attribute EventHandler oncontextmenu; + attribute EventHandler oncuechange; + attribute EventHandler ondblclick; + attribute EventHandler ondrag; + attribute EventHandler ondragend; + attribute EventHandler ondragenter; + // attribute EventHandler ondragexit; + attribute EventHandler ondragleave; + attribute EventHandler ondragover; + attribute EventHandler ondragstart; + attribute EventHandler ondrop; + attribute EventHandler ondurationchange; + attribute EventHandler onemptied; + attribute EventHandler onended; + attribute EventHandler onerror; + attribute EventHandler onfocus; + attribute EventHandler oninput; + attribute EventHandler oninvalid; + attribute EventHandler onkeydown; + attribute EventHandler onkeypress; + attribute EventHandler onkeyup; + attribute EventHandler onload; + attribute EventHandler onloadeddata; + attribute EventHandler onloadedmetadata; + attribute EventHandler onloadstart; + attribute EventHandler onmousedown; + [LenientThis] attribute EventHandler onmouseenter; + [LenientThis] attribute EventHandler onmouseleave; + attribute EventHandler onmousemove; + attribute EventHandler onmouseout; + attribute EventHandler onmouseover; + attribute EventHandler onmouseup; + attribute EventHandler onmousewheel; + attribute EventHandler onpause; + attribute EventHandler onplay; + attribute EventHandler onplaying; + attribute EventHandler onprogress; + attribute EventHandler onratechange; + attribute EventHandler onreset; + attribute EventHandler onresize; + attribute EventHandler onscroll; + attribute EventHandler onseeked; + attribute EventHandler onseeking; + attribute EventHandler onselect; + // attribute EventHandler onshow; + // attribute EventHandler onsort; + attribute EventHandler onstalled; + attribute EventHandler onsubmit; + attribute EventHandler onsuspend; + attribute EventHandler ontimeupdate; + attribute EventHandler ontoggle; + attribute EventHandler onvolumechange; + attribute EventHandler onwaiting; + + + // Additions that are not yet part of the standard. + + [NotEnumerable] attribute EventHandler onsearch; + [NotEnumerable] attribute EventHandler onwheel; + [NotEnumerable, Conditional=TOUCH_EVENTS] attribute EventHandler ontouchcancel; + [NotEnumerable, Conditional=TOUCH_EVENTS] attribute EventHandler ontouchend; + [NotEnumerable, Conditional=TOUCH_EVENTS] attribute EventHandler ontouchmove; + [NotEnumerable, Conditional=TOUCH_EVENTS] attribute EventHandler ontouchstart; + [NotEnumerable, Conditional=TOUCH_EVENTS] attribute EventHandler ontouchforcechange; + [NotEnumerable, Conditional=MOUSE_FORCE_EVENTS] attribute EventHandler onwebkitmouseforcechanged; + [NotEnumerable, Conditional=MOUSE_FORCE_EVENTS] attribute EventHandler onwebkitmouseforcedown; + [NotEnumerable, Conditional=MOUSE_FORCE_EVENTS] attribute EventHandler onwebkitmouseforcewillbegin; + [NotEnumerable, Conditional=MOUSE_FORCE_EVENTS] attribute EventHandler onwebkitmouseforceup; + [NotEnumerable, Conditional=WILL_REVEAL_EDGE_EVENTS] attribute EventHandler onwebkitwillrevealbottom; + [NotEnumerable, Conditional=WILL_REVEAL_EDGE_EVENTS] attribute EventHandler onwebkitwillrevealleft; + [NotEnumerable, Conditional=WILL_REVEAL_EDGE_EVENTS] attribute EventHandler onwebkitwillrevealright; + [NotEnumerable, Conditional=WILL_REVEAL_EDGE_EVENTS] attribute EventHandler onwebkitwillrevealtop; +}; diff --git a/Source/WebCore/dom/HashChangeEvent.h b/Source/WebCore/dom/HashChangeEvent.h index 85cb0b53a..f2c3d94a8 100644 --- a/Source/WebCore/dom/HashChangeEvent.h +++ b/Source/WebCore/dom/HashChangeEvent.h @@ -18,38 +18,33 @@ * */ -#ifndef HashChangeEvent_h -#define HashChangeEvent_h +#pragma once #include "Event.h" #include "EventNames.h" namespace WebCore { -struct HashChangeEventInit : public EventInit { - HashChangeEventInit() - { - }; - - String oldURL; - String newURL; -}; - -class HashChangeEvent : public Event { +class HashChangeEvent final : public Event { public: - static PassRefPtr<HashChangeEvent> create() + static Ref<HashChangeEvent> create(const String& oldURL, const String& newURL) { - return adoptRef(new HashChangeEvent); + return adoptRef(*new HashChangeEvent(oldURL, newURL)); } - static PassRefPtr<HashChangeEvent> create(const String& oldURL, const String& newURL) + static Ref<HashChangeEvent> createForBindings() { - return adoptRef(new HashChangeEvent(oldURL, newURL)); + return adoptRef(*new HashChangeEvent); } - static PassRefPtr<HashChangeEvent> create(const AtomicString& type, const HashChangeEventInit& initializer) + struct Init : EventInit { + String oldURL; + String newURL; + }; + + static Ref<HashChangeEvent> create(const AtomicString& type, const Init& initializer, IsTrusted isTrusted = IsTrusted::No) { - return adoptRef(new HashChangeEvent(type, initializer)); + return adoptRef(*new HashChangeEvent(type, initializer, isTrusted)); } void initHashChangeEvent(const AtomicString& eventType, bool canBubble, bool cancelable, const String& oldURL, const String& newURL) @@ -66,7 +61,7 @@ public: const String& oldURL() const { return m_oldURL; } const String& newURL() const { return m_newURL; } - virtual EventInterface eventInterface() const override { return HashChangeEventInterfaceType; } + EventInterface eventInterface() const override { return HashChangeEventInterfaceType; } private: HashChangeEvent() @@ -80,8 +75,8 @@ private: { } - HashChangeEvent(const AtomicString& type, const HashChangeEventInit& initializer) - : Event(type, initializer) + HashChangeEvent(const AtomicString& type, const Init& initializer, IsTrusted isTrusted) + : Event(type, initializer, isTrusted) , m_oldURL(initializer.oldURL) , m_newURL(initializer.newURL) { @@ -92,5 +87,3 @@ private: }; } // namespace WebCore - -#endif // HashChangeEvent_h diff --git a/Source/WebCore/dom/HashChangeEvent.idl b/Source/WebCore/dom/HashChangeEvent.idl index 8674ffcd9..e72ff8d35 100644 --- a/Source/WebCore/dom/HashChangeEvent.idl +++ b/Source/WebCore/dom/HashChangeEvent.idl @@ -19,14 +19,20 @@ // Introduced in http://www.whatwg.org/specs/web-apps/current-work/multipage/history.html#event-hashchange [ - ConstructorTemplate=Event, + Constructor(DOMString type, optional HashChangeEventInit eventInitDict), ] interface HashChangeEvent : Event { - void initHashChangeEvent([Default=Undefined] optional DOMString type, - [Default=Undefined] optional boolean canBubble, - [Default=Undefined] optional boolean cancelable, - [Default=Undefined] optional DOMString oldURL, - [Default=Undefined] optional DOMString newURL); - [InitializedByEventConstructor] readonly attribute DOMString oldURL; - [InitializedByEventConstructor] readonly attribute DOMString newURL; + // FIXME: Using "undefined" as default parameter value is wrong. + void initHashChangeEvent(optional DOMString type = "undefined", + optional boolean canBubble = false, + optional boolean cancelable = false, + optional USVString oldURL = "undefined", + optional USVString newURL = "undefined"); + + readonly attribute USVString oldURL; + readonly attribute USVString newURL; }; +dictionary HashChangeEventInit : EventInit { + USVString oldURL = ""; + USVString newURL = ""; +}; diff --git a/Source/WebCore/dom/IconURL.cpp b/Source/WebCore/dom/IconURL.cpp deleted file mode 100644 index 646f6c5a0..000000000 --- a/Source/WebCore/dom/IconURL.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "IconURL.h" - -namespace WebCore { - -IconURL IconURL::defaultIconURL(const URL& url, IconType type) -{ - IconURL result(url, emptyString(), emptyString(), type); - result.m_isDefaultIcon = true; - return result; -} - -bool operator==(const IconURL& lhs, const IconURL& rhs) -{ - return lhs.m_iconType == rhs.m_iconType - && lhs.m_isDefaultIcon == rhs.m_isDefaultIcon - && lhs.m_iconURL == rhs.m_iconURL - && lhs.m_sizes == rhs.m_sizes - && lhs.m_mimeType == rhs.m_mimeType; -} - -} - diff --git a/Source/WebCore/dom/IconURL.h b/Source/WebCore/dom/IconURL.h deleted file mode 100644 index 7916ae0f7..000000000 --- a/Source/WebCore/dom/IconURL.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IconURL_h -#define IconURL_h - -#include "URL.h" - -namespace WebCore { - -#if ENABLE(TOUCH_ICON_LOADING) -#define ICON_COUNT 3 -#else -#define ICON_COUNT 1 -#endif - -enum IconType { - InvalidIcon = 0, - Favicon = 1, - TouchIcon = 1 << 1, - TouchPrecomposedIcon = 1 << 2, -}; - -struct IconURL { - IconType m_iconType; - String m_sizes; - String m_mimeType; - URL m_iconURL; - bool m_isDefaultIcon; - - IconURL() - : m_iconType(InvalidIcon) - , m_isDefaultIcon(false) - { - } - - IconURL(const URL& url, const String& sizes, const String& mimeType, IconType type) - : m_iconType(type) - , m_sizes(sizes) - , m_mimeType(mimeType) - , m_iconURL(url) - , m_isDefaultIcon(false) - { - } - - static IconURL defaultIconURL(const URL&, IconType); -}; - -bool operator==(const IconURL&, const IconURL&); - -typedef Vector<IconURL, ICON_COUNT> IconURLs; - -} - -#endif // IconURL_h diff --git a/Source/WebCore/dom/IdTargetObserver.h b/Source/WebCore/dom/IdTargetObserver.h index f9007c478..2e26bf276 100644 --- a/Source/WebCore/dom/IdTargetObserver.h +++ b/Source/WebCore/dom/IdTargetObserver.h @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IdTargetObserver_h -#define IdTargetObserver_h +#pragma once #include <wtf/text/AtomicString.h> @@ -46,5 +45,3 @@ private: }; } // namespace WebCore - -#endif // IdTargetObserver_h diff --git a/Source/WebCore/dom/IdTargetObserverRegistry.cpp b/Source/WebCore/dom/IdTargetObserverRegistry.cpp index 914dd485d..bf7e54a8c 100644 --- a/Source/WebCore/dom/IdTargetObserverRegistry.cpp +++ b/Source/WebCore/dom/IdTargetObserverRegistry.cpp @@ -30,11 +30,6 @@ namespace WebCore { -PassOwnPtr<IdTargetObserverRegistry> IdTargetObserverRegistry::create() -{ - return adoptPtr(new IdTargetObserverRegistry()); -} - void IdTargetObserverRegistry::addObserver(const AtomicString& id, IdTargetObserver* observer) { if (id.isEmpty()) @@ -42,7 +37,7 @@ void IdTargetObserverRegistry::addObserver(const AtomicString& id, IdTargetObser IdToObserverSetMap::AddResult result = m_registry.add(id.impl(), nullptr); if (result.isNewEntry) - result.iterator->value = adoptPtr(new ObserverSet()); + result.iterator->value = std::make_unique<ObserverSet>(); result.iterator->value->add(observer); } @@ -70,15 +65,15 @@ void IdTargetObserverRegistry::notifyObserversInternal(const AtomicStringImpl& i Vector<IdTargetObserver*> copy; copyToVector(*m_notifyingObserversInSet, copy); - for (Vector<IdTargetObserver*>::const_iterator it = copy.begin(); it != copy.end(); ++it) { - if (m_notifyingObserversInSet->contains(*it)) - (*it)->idTargetChanged(); + for (auto& observer : copy) { + if (m_notifyingObserversInSet->contains(observer)) + observer->idTargetChanged(); } if (m_notifyingObserversInSet->isEmpty()) m_registry.remove(&id); - m_notifyingObserversInSet = 0; + m_notifyingObserversInSet = nullptr; } } // namespace WebCore diff --git a/Source/WebCore/dom/IdTargetObserverRegistry.h b/Source/WebCore/dom/IdTargetObserverRegistry.h index 29c40f763..edac13dd6 100644 --- a/Source/WebCore/dom/IdTargetObserverRegistry.h +++ b/Source/WebCore/dom/IdTargetObserverRegistry.h @@ -23,13 +23,12 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IdTargetObserverRegistry_h -#define IdTargetObserverRegistry_h +#pragma once +#include <memory> #include <wtf/Forward.h> #include <wtf/HashMap.h> #include <wtf/HashSet.h> -#include <wtf/PassOwnPtr.h> #include <wtf/text/AtomicString.h> namespace WebCore { @@ -40,20 +39,20 @@ class IdTargetObserverRegistry { WTF_MAKE_FAST_ALLOCATED; friend class IdTargetObserver; public: - static PassOwnPtr<IdTargetObserverRegistry> create(); + IdTargetObserverRegistry() { } + void notifyObservers(const AtomicString& id); void notifyObservers(const AtomicStringImpl& id); private: - IdTargetObserverRegistry() : m_notifyingObserversInSet(0) { } void addObserver(const AtomicString& id, IdTargetObserver*); void removeObserver(const AtomicString& id, IdTargetObserver*); void notifyObserversInternal(const AtomicStringImpl& id); typedef HashSet<IdTargetObserver*> ObserverSet; - typedef HashMap<const AtomicStringImpl*, OwnPtr<ObserverSet>> IdToObserverSetMap; + typedef HashMap<const AtomicStringImpl*, std::unique_ptr<ObserverSet>> IdToObserverSetMap; IdToObserverSetMap m_registry; - ObserverSet* m_notifyingObserversInSet; + ObserverSet* m_notifyingObserversInSet { nullptr }; }; inline void IdTargetObserverRegistry::notifyObservers(const AtomicString& id) @@ -72,5 +71,3 @@ inline void IdTargetObserverRegistry::notifyObservers(const AtomicStringImpl& id } } // namespace WebCore - -#endif // IdTargetObserverRegistry_h diff --git a/Source/WebCore/dom/IgnoreDestructiveWriteCountIncrementer.h b/Source/WebCore/dom/IgnoreDestructiveWriteCountIncrementer.h index 26f7fd08d..2f98ddb0a 100644 --- a/Source/WebCore/dom/IgnoreDestructiveWriteCountIncrementer.h +++ b/Source/WebCore/dom/IgnoreDestructiveWriteCountIncrementer.h @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef IgnoreDestructiveWriteCountIncrementer_h -#define IgnoreDestructiveWriteCountIncrementer_h +#pragma once #include "Document.h" @@ -34,7 +33,7 @@ class IgnoreDestructiveWriteCountIncrementer { WTF_MAKE_NONCOPYABLE(IgnoreDestructiveWriteCountIncrementer); public: explicit IgnoreDestructiveWriteCountIncrementer(Document* document) - : m_count(document ? &document->m_ignoreDestructiveWriteCount : 0) + : m_count(document ? &document->m_ignoreDestructiveWriteCount : nullptr) { if (!m_count) return; @@ -52,6 +51,4 @@ private: unsigned* m_count; }; -} - -#endif +} // namespace WebCore diff --git a/Source/WebCore/dom/IgnoreOpensDuringUnloadCountIncrementer.h b/Source/WebCore/dom/IgnoreOpensDuringUnloadCountIncrementer.h new file mode 100644 index 000000000..5f6e7e9bf --- /dev/null +++ b/Source/WebCore/dom/IgnoreOpensDuringUnloadCountIncrementer.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "Document.h" + +namespace WebCore { + +class IgnoreOpensDuringUnloadCountIncrementer { + WTF_MAKE_NONCOPYABLE(IgnoreOpensDuringUnloadCountIncrementer); +public: + explicit IgnoreOpensDuringUnloadCountIncrementer(Document* document) + : m_count(document ? &document->m_ignoreOpensDuringUnloadCount : nullptr) + { + if (!m_count) + return; + ++(*m_count); + } + + ~IgnoreOpensDuringUnloadCountIncrementer() + { + if (!m_count) + return; + --(*m_count); + } + +private: + unsigned* m_count; +}; + +} // namespace WebCore diff --git a/Source/WebCore/dom/InlineClassicScript.cpp b/Source/WebCore/dom/InlineClassicScript.cpp new file mode 100644 index 000000000..5e28ade0b --- /dev/null +++ b/Source/WebCore/dom/InlineClassicScript.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2017 Yusuke Suzuki <utatane.tea@gmail.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "InlineClassicScript.h" + +#include "Element.h" +#include "ScriptElement.h" + +namespace WebCore { + +Ref<InlineClassicScript> InlineClassicScript::create(ScriptElement& scriptElement) +{ + auto& element = scriptElement.element(); + return adoptRef(*new InlineClassicScript( + element.attributeWithoutSynchronization(HTMLNames::nonceAttr), + element.attributeWithoutSynchronization(HTMLNames::crossoriginAttr), + scriptElement.scriptCharset(), + element.localName(), + element.isInUserAgentShadowTree())); +} + +} diff --git a/Source/WebCore/dom/InlineClassicScript.h b/Source/WebCore/dom/InlineClassicScript.h new file mode 100644 index 000000000..9fa9f31f4 --- /dev/null +++ b/Source/WebCore/dom/InlineClassicScript.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2017 Yusuke Suzuki <utatane.tea@gmail.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "ScriptElementCachedScriptFetcher.h" + +namespace WebCore { + +class ScriptElement; + +class InlineClassicScript final : public ScriptElementCachedScriptFetcher { +public: + static Ref<InlineClassicScript> create(ScriptElement&); + + bool isClassicScript() const final { return true; } + bool isModuleScript() const final { return false; } + +private: + InlineClassicScript(const String& nonce, const String& crossOriginMode, const String& charset, const AtomicString& initiatorName, bool isInUserAgentShadowTree) + : ScriptElementCachedScriptFetcher(nonce, crossOriginMode, charset, initiatorName, isInUserAgentShadowTree) + { + } +}; + +} diff --git a/Source/WebCore/dom/InlineStyleSheetOwner.cpp b/Source/WebCore/dom/InlineStyleSheetOwner.cpp index def726f58..a25263248 100644 --- a/Source/WebCore/dom/InlineStyleSheetOwner.cpp +++ b/Source/WebCore/dom/InlineStyleSheetOwner.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2006, 2007 Rob Buis - * Copyright (C) 2008, 2013 Apple, Inc. All rights reserved. + * Copyright (C) 2008-2016 Apple, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -26,130 +26,199 @@ #include "MediaList.h" #include "MediaQueryEvaluator.h" #include "ScriptableDocumentParser.h" +#include "ShadowRoot.h" +#include "StyleScope.h" #include "StyleSheetContents.h" #include "TextNodeTraversal.h" -#include <wtf/text/StringBuilder.h> +#include <wtf/HashMap.h> +#include <wtf/NeverDestroyed.h> namespace WebCore { +using InlineStyleSheetCacheKey = std::pair<String, CSSParserContext>; +using InlineStyleSheetCache = HashMap<InlineStyleSheetCacheKey, RefPtr<StyleSheetContents>>; + +static InlineStyleSheetCache& inlineStyleSheetCache() +{ + static NeverDestroyed<InlineStyleSheetCache> cache; + return cache; +} + +static CSSParserContext parserContextForElement(const Element& element) +{ + auto* shadowRoot = element.containingShadowRoot(); + // User agent shadow trees can't contain document-relative URLs. Use blank URL as base allowing cross-document sharing. + auto& baseURL = shadowRoot && shadowRoot->mode() == ShadowRootMode::UserAgent ? blankURL() : element.document().baseURL(); + + CSSParserContext result = CSSParserContext { element.document(), baseURL, element.document().characterSetWithUTF8Fallback() }; + if (shadowRoot && shadowRoot->mode() == ShadowRootMode::UserAgent) + result.mode = UASheetMode; + return result; +} + +static std::optional<InlineStyleSheetCacheKey> makeInlineStyleSheetCacheKey(const String& text, const Element& element) +{ + // Only cache for shadow trees. Main document inline stylesheets are generally unique and can't be shared between documents. + // FIXME: This could be relaxed when a stylesheet does not contain document-relative URLs (or #urls). + if (!element.isInShadowTree()) + return { }; + + return std::make_pair(text, parserContextForElement(element)); +} + InlineStyleSheetOwner::InlineStyleSheetOwner(Document& document, bool createdByParser) : m_isParsingChildren(createdByParser) , m_loading(false) - , m_startLineNumber(WTF::OrdinalNumber::beforeFirst()) + , m_startTextPosition() { if (createdByParser && document.scriptableDocumentParser() && !document.isInDocumentWrite()) - m_startLineNumber = document.scriptableDocumentParser()->textPosition().m_line; + m_startTextPosition = document.scriptableDocumentParser()->textPosition(); } InlineStyleSheetOwner::~InlineStyleSheetOwner() { + if (m_sheet) + clearSheet(); } -void InlineStyleSheetOwner::insertedIntoDocument(Document& document, Element& element) +void InlineStyleSheetOwner::insertedIntoDocument(Element& element) { - document.styleSheetCollection().addStyleSheetCandidateNode(element, m_isParsingChildren); + m_styleScope = &Style::Scope::forNode(element); + m_styleScope->addStyleSheetCandidateNode(element, m_isParsingChildren); if (m_isParsingChildren) return; createSheetFromTextContents(element); } -void InlineStyleSheetOwner::removedFromDocument(Document& document, Element& element) +void InlineStyleSheetOwner::removedFromDocument(Element& element) { - document.styleSheetCollection().removeStyleSheetCandidateNode(element); - + if (m_styleScope) { + m_styleScope->removeStyleSheetCandidateNode(element); + m_styleScope = nullptr; + } if (m_sheet) clearSheet(); - - // If we're in document teardown, then we don't need to do any notification of our sheet's removal. - if (document.hasLivingRenderTree()) - document.styleResolverChanged(DeferRecalcStyle); } -void InlineStyleSheetOwner::clearDocumentData(Document& document, Element& element) +void InlineStyleSheetOwner::clearDocumentData(Element& element) { if (m_sheet) m_sheet->clearOwnerNode(); - if (!element.inDocument()) - return; - document.styleSheetCollection().removeStyleSheetCandidateNode(element); + if (m_styleScope) { + m_styleScope->removeStyleSheetCandidateNode(element); + m_styleScope = nullptr; + } } void InlineStyleSheetOwner::childrenChanged(Element& element) { if (m_isParsingChildren) return; - if (!element.inDocument()) + if (!element.isConnected()) return; createSheetFromTextContents(element); } void InlineStyleSheetOwner::finishParsingChildren(Element& element) { - if (element.inDocument()) + if (element.isConnected()) createSheetFromTextContents(element); m_isParsingChildren = false; } void InlineStyleSheetOwner::createSheetFromTextContents(Element& element) { - createSheet(element, TextNodeTraversal::contentsAsString(&element)); + createSheet(element, TextNodeTraversal::contentsAsString(element)); } void InlineStyleSheetOwner::clearSheet() { ASSERT(m_sheet); - m_sheet.release()->clearOwnerNode(); + auto sheet = WTFMove(m_sheet); + sheet->clearOwnerNode(); } inline bool isValidCSSContentType(Element& element, const AtomicString& type) { - DEFINE_STATIC_LOCAL(const AtomicString, cssContentType, ("text/css", AtomicString::ConstructFromLiteral)); if (type.isEmpty()) return true; - return element.isHTMLElement() ? equalIgnoringCase(type, cssContentType) : type == cssContentType; + // FIXME: Should MIME types really be case sensitive in XML documents? Doesn't seem like they should, + // even though other things are case sensitive in that context. MIME types should never be case sensitive. + // We should verify this and then remove the isHTMLElement check here. + static NeverDestroyed<const AtomicString> cssContentType("text/css", AtomicString::ConstructFromLiteral); + return element.isHTMLElement() ? equalLettersIgnoringASCIICase(type, "text/css") : type == cssContentType; } void InlineStyleSheetOwner::createSheet(Element& element, const String& text) { - ASSERT(element.inDocument()); + ASSERT(element.isConnected()); Document& document = element.document(); if (m_sheet) { - if (m_sheet->isLoading()) - document.styleSheetCollection().removePendingSheet(); + if (m_sheet->isLoading() && m_styleScope) + m_styleScope->removePendingSheet(); clearSheet(); } if (!isValidCSSContentType(element, m_contentType)) return; - if (!document.contentSecurityPolicy()->allowInlineStyle(document.url(), m_startLineNumber)) + + ASSERT(document.contentSecurityPolicy()); + const ContentSecurityPolicy& contentSecurityPolicy = *document.contentSecurityPolicy(); + bool hasKnownNonce = contentSecurityPolicy.allowStyleWithNonce(element.attributeWithoutSynchronization(HTMLNames::nonceAttr), element.isInUserAgentShadowTree()); + if (!contentSecurityPolicy.allowInlineStyle(document.url(), m_startTextPosition.m_line, text, hasKnownNonce)) return; - RefPtr<MediaQuerySet> mediaQueries; - if (element.isHTMLElement()) - mediaQueries = MediaQuerySet::createAllowingDescriptionSyntax(m_media); - else - mediaQueries = MediaQuerySet::create(m_media); + RefPtr<MediaQuerySet> mediaQueries = MediaQuerySet::create(m_media); MediaQueryEvaluator screenEval(ASCIILiteral("screen"), true); MediaQueryEvaluator printEval(ASCIILiteral("print"), true); - if (!screenEval.eval(mediaQueries.get()) && !printEval.eval(mediaQueries.get())) + if (!screenEval.evaluate(*mediaQueries) && !printEval.evaluate(*mediaQueries)) return; - document.styleSheetCollection().addPendingSheet(); + if (m_styleScope) + m_styleScope->addPendingSheet(); + + auto cacheKey = makeInlineStyleSheetCacheKey(text, element); + if (cacheKey) { + if (auto* cachedSheet = inlineStyleSheetCache().get(*cacheKey)) { + ASSERT(cachedSheet->isCacheable()); + m_sheet = CSSStyleSheet::createInline(*cachedSheet, element, m_startTextPosition); + m_sheet->setMediaQueries(mediaQueries.releaseNonNull()); + m_sheet->setTitle(element.title()); + + sheetLoaded(element); + element.notifyLoadedSheetAndAllCriticalSubresources(false); + return; + } + } m_loading = true; - m_sheet = CSSStyleSheet::createInline(element, URL(), document.inputEncoding()); - m_sheet->setMediaQueries(mediaQueries.release()); + auto contents = StyleSheetContents::create(String(), parserContextForElement(element)); + + m_sheet = CSSStyleSheet::createInline(contents.get(), element, m_startTextPosition); + m_sheet->setMediaQueries(mediaQueries.releaseNonNull()); m_sheet->setTitle(element.title()); - m_sheet->contents().parseStringAtLine(text, m_startLineNumber.zeroBasedInt(), m_isParsingChildren); + + contents->parseString(text); m_loading = false; - if (m_sheet) - m_sheet->contents().checkLoaded(); + contents->checkLoaded(); + + if (cacheKey && contents->isCacheable()) { + m_sheet->contents().addedToMemoryCache(); + inlineStyleSheetCache().add(*cacheKey, &m_sheet->contents()); + + // Prevent pathological growth. + const size_t maximumInlineStyleSheetCacheSize = 50; + if (inlineStyleSheetCache().size() > maximumInlineStyleSheetCacheSize) { + inlineStyleSheetCache().begin()->value->removedFromMemoryCache(); + inlineStyleSheetCache().remove(inlineStyleSheetCache().begin()); + } + } } bool InlineStyleSheetOwner::isLoading() const @@ -159,18 +228,26 @@ bool InlineStyleSheetOwner::isLoading() const return m_sheet && m_sheet->isLoading(); } -bool InlineStyleSheetOwner::sheetLoaded(Document& document) +bool InlineStyleSheetOwner::sheetLoaded(Element&) { if (isLoading()) return false; - document.styleSheetCollection().removePendingSheet(); + if (m_styleScope) + m_styleScope->removePendingSheet(); + return true; } -void InlineStyleSheetOwner::startLoadingDynamicSheet(Document& document) +void InlineStyleSheetOwner::startLoadingDynamicSheet(Element&) +{ + if (m_styleScope) + m_styleScope->addPendingSheet(); +} + +void InlineStyleSheetOwner::clearCache() { - document.styleSheetCollection().addPendingSheet(); + inlineStyleSheetCache().clear(); } } diff --git a/Source/WebCore/dom/InlineStyleSheetOwner.h b/Source/WebCore/dom/InlineStyleSheetOwner.h index 6f422c90d..06fae80d1 100644 --- a/Source/WebCore/dom/InlineStyleSheetOwner.h +++ b/Source/WebCore/dom/InlineStyleSheetOwner.h @@ -19,8 +19,7 @@ * */ -#ifndef InlineStyleSheetOwner_h -#define InlineStyleSheetOwner_h +#pragma once #include "CSSStyleSheet.h" #include <wtf/text/TextPosition.h> @@ -41,15 +40,19 @@ public: CSSStyleSheet* sheet() const { return m_sheet.get(); } bool isLoading() const; - bool sheetLoaded(Document&); - void startLoadingDynamicSheet(Document&); + bool sheetLoaded(Element&); + void startLoadingDynamicSheet(Element&); - void insertedIntoDocument(Document&, Element&); - void removedFromDocument(Document&, Element&); - void clearDocumentData(Document&, Element&); + void insertedIntoDocument(Element&); + void removedFromDocument(Element&); + void clearDocumentData(Element&); void childrenChanged(Element&); void finishParsingChildren(Element&); + Style::Scope* styleScope() { return m_styleScope; } + + static void clearCache(); + private: void createSheet(Element&, const String& text); void createSheetFromTextContents(Element&); @@ -57,12 +60,11 @@ private: bool m_isParsingChildren; bool m_loading; - WTF::OrdinalNumber m_startLineNumber; + WTF::TextPosition m_startTextPosition; AtomicString m_contentType; AtomicString m_media; RefPtr<CSSStyleSheet> m_sheet; + Style::Scope* m_styleScope { nullptr }; }; -} - -#endif +} // namespace WebCore diff --git a/Source/WebCore/dom/InputEvent.cpp b/Source/WebCore/dom/InputEvent.cpp new file mode 100644 index 000000000..722778d04 --- /dev/null +++ b/Source/WebCore/dom/InputEvent.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "InputEvent.h" + +#include "DOMWindow.h" +#include "DataTransfer.h" +#include "Node.h" +#include <wtf/NeverDestroyed.h> +#include <wtf/Vector.h> + +namespace WebCore { + +Ref<InputEvent> InputEvent::create(const AtomicString& eventType, const String& inputType, bool canBubble, bool cancelable, WebCore::DOMWindow *view, const String& data, RefPtr<DataTransfer>&& dataTransfer, const Vector<RefPtr<StaticRange>>& targetRanges, int detail) +{ + return adoptRef(*new InputEvent(eventType, inputType, canBubble, cancelable, view, data, WTFMove(dataTransfer), targetRanges, detail)); +} + +InputEvent::InputEvent(const AtomicString& eventType, const String& inputType, bool canBubble, bool cancelable, DOMWindow* view, const String& data, RefPtr<DataTransfer>&& dataTransfer, const Vector<RefPtr<StaticRange>>& targetRanges, int detail) + : UIEvent(eventType, canBubble, cancelable, view, detail) + , m_inputType(inputType) + , m_data(data) + , m_dataTransfer(dataTransfer) + , m_targetRanges(targetRanges) +{ +} + +InputEvent::InputEvent(const AtomicString& eventType, const Init& initializer, IsTrusted isTrusted) + : UIEvent(eventType, initializer, isTrusted) + , m_inputType(emptyString()) + , m_data(initializer.data) +{ +} + +RefPtr<DataTransfer> InputEvent::dataTransfer() const +{ + return m_dataTransfer; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/InputEvent.h b/Source/WebCore/dom/InputEvent.h new file mode 100644 index 000000000..cad8dce7b --- /dev/null +++ b/Source/WebCore/dom/InputEvent.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "StaticRange.h" +#include "UIEvent.h" + +namespace WebCore { + +class DOMWindow; +class DataTransfer; + +class InputEvent final : public UIEvent { +public: + struct Init : UIEventInit { + String data; + }; + + static Ref<InputEvent> create(const AtomicString& eventType, const String& inputType, bool canBubble, bool cancelable, DOMWindow* view, const String& data, RefPtr<DataTransfer>&&, const Vector<RefPtr<StaticRange>>& targetRanges, int detail); + static Ref<InputEvent> create(const AtomicString& type, const Init& initializer, IsTrusted isTrusted = IsTrusted::No) + { + return adoptRef(*new InputEvent(type, initializer, isTrusted)); + } + + InputEvent(const AtomicString& eventType, const String& inputType, bool canBubble, bool cancelable, DOMWindow*, const String& data, RefPtr<DataTransfer>&&, const Vector<RefPtr<StaticRange>>& targetRanges, int detail); + InputEvent(const AtomicString& eventType, const Init&, IsTrusted); + + bool isInputEvent() const override { return true; } + EventInterface eventInterface() const final { return InputEventInterfaceType; } + const String& inputType() const { return m_inputType; } + const String& data() const { return m_data; } + RefPtr<DataTransfer> dataTransfer() const; + const Vector<RefPtr<StaticRange>>& getTargetRanges() { return m_targetRanges; } + +private: + String m_inputType; + String m_data; + RefPtr<DataTransfer> m_dataTransfer; + Vector<RefPtr<StaticRange>> m_targetRanges; +}; + +} // namespace WebCore + +SPECIALIZE_TYPE_TRAITS_EVENT(InputEvent) diff --git a/Source/WebCore/dom/InputEvent.idl b/Source/WebCore/dom/InputEvent.idl new file mode 100644 index 000000000..1f765de9a --- /dev/null +++ b/Source/WebCore/dom/InputEvent.idl @@ -0,0 +1,39 @@ +/* +* Copyright (C) 2016 Apple, Inc. All Rights Reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +[ + Constructor(DOMString type, optional InputEventInit eventInitDict), + EnabledAtRuntime=InputEvents, +] interface InputEvent : UIEvent { + readonly attribute DOMString inputType; + readonly attribute DOMString? data; + readonly attribute DataTransfer? dataTransfer; + + sequence<StaticRange> getTargetRanges(); +}; + +dictionary InputEventInit : UIEventInit { + DOMString? data = null; +}; diff --git a/Source/WebCore/dom/KeyboardEvent.cpp b/Source/WebCore/dom/KeyboardEvent.cpp index c0e16f841..2bd931fc4 100644 --- a/Source/WebCore/dom/KeyboardEvent.cpp +++ b/Source/WebCore/dom/KeyboardEvent.cpp @@ -24,11 +24,12 @@ #include "KeyboardEvent.h" #include "Document.h" +#include "Editor.h" #include "EventDispatcher.h" #include "EventHandler.h" +#include "EventNames.h" #include "Frame.h" #include "PlatformKeyboardEvent.h" -#include "Settings.h" #include "WindowsKeyboardCodes.h" namespace WebCore { @@ -90,36 +91,58 @@ static inline KeyboardEvent::KeyLocationCode keyLocationCode(const PlatformKeybo } } -KeyboardEventInit::KeyboardEventInit() - : location(0) - , ctrlKey(false) - , altKey(false) - , shiftKey(false) - , metaKey(false) -{ -} +KeyboardEvent::KeyboardEvent() = default; -KeyboardEvent::KeyboardEvent() - : m_location(DOM_KEY_LOCATION_STANDARD) - , m_altGraphKey(false) +KeyboardEvent::KeyboardEvent(const PlatformKeyboardEvent& key, DOMWindow* view) + : UIEventWithKeyState(eventTypeForKeyboardEventType(key.type()) + , true, true, key.timestamp(), view, 0, key.ctrlKey(), key.altKey(), key.shiftKey() + , key.metaKey(), false, key.modifiers().contains(PlatformEvent::Modifier::CapsLockKey)) + , m_keyEvent(std::make_unique<PlatformKeyboardEvent>(key)) +#if ENABLE(KEYBOARD_KEY_ATTRIBUTE) + , m_key(key.key()) +#endif +#if ENABLE(KEYBOARD_CODE_ATTRIBUTE) + , m_code(key.code()) +#endif + , m_keyIdentifier(key.keyIdentifier()) + , m_location(keyLocationCode(key)) + , m_repeat(key.isAutoRepeat()) + , m_isComposing(view && view->frame() && view->frame()->editor().hasComposition()) +#if PLATFORM(COCOA) +#if USE(APPKIT) + , m_handledByInputMethod(key.handledByInputMethod()) + , m_keypressCommands(key.commands()) +#else + , m_handledByInputMethod(false) +#endif +#endif { } -KeyboardEvent::KeyboardEvent(const PlatformKeyboardEvent& key, AbstractView* view) - : UIEventWithKeyState(eventTypeForKeyboardEventType(key.type()), - true, true, key.timestamp(), view, 0, key.ctrlKey(), key.altKey(), key.shiftKey(), key.metaKey()) - , m_keyEvent(adoptPtr(new PlatformKeyboardEvent(key))) - , m_keyIdentifier(key.keyIdentifier()) - , m_location(keyLocationCode(key)) - , m_altGraphKey(false) +// FIXME: This method should be get ride of in the future. +// DO NOT USE IT! +KeyboardEvent::KeyboardEvent(WTF::HashTableDeletedValueType) { } -KeyboardEvent::KeyboardEvent(const AtomicString& eventType, const KeyboardEventInit& initializer) - : UIEventWithKeyState(eventType, initializer.bubbles, initializer.cancelable, initializer.view, initializer.detail, initializer.ctrlKey, initializer.altKey, initializer.shiftKey, initializer.metaKey) +KeyboardEvent::KeyboardEvent(const AtomicString& eventType, const Init& initializer, IsTrusted isTrusted) + : UIEventWithKeyState(eventType, initializer, isTrusted) +#if ENABLE(KEYBOARD_KEY_ATTRIBUTE) + , m_key(initializer.key) +#endif +#if ENABLE(KEYBOARD_CODE_ATTRIBUTE) + , m_code(initializer.code) +#endif , m_keyIdentifier(initializer.keyIdentifier) - , m_location(initializer.location) - , m_altGraphKey(false) + , m_location(initializer.keyLocation ? *initializer.keyLocation : initializer.location) + , m_repeat(initializer.repeat) + , m_isComposing(initializer.isComposing) + , m_charCode(initializer.charCode) + , m_keyCode(initializer.keyCode) + , m_which(initializer.which) +#if PLATFORM(COCOA) + , m_handledByInputMethod(false) +#endif { } @@ -127,7 +150,7 @@ KeyboardEvent::~KeyboardEvent() { } -void KeyboardEvent::initKeyboardEvent(const AtomicString& type, bool canBubble, bool cancelable, AbstractView* view, +void KeyboardEvent::initKeyboardEvent(const AtomicString& type, bool canBubble, bool cancelable, DOMWindow* view, const String &keyIdentifier, unsigned location, bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, bool altGraphKey) { @@ -155,11 +178,19 @@ bool KeyboardEvent::getModifierState(const String& keyIdentifier) const return altKey(); if (keyIdentifier == "Meta") return metaKey(); + if (keyIdentifier == "AltGraph") + return altGraphKey(); + if (keyIdentifier == "CapsLock") + return capsLockKey(); + // FIXME: The specification also has Fn, FnLock, Hyper, NumLock, Super, ScrollLock, Symbol, SymbolLock. return false; } int KeyboardEvent::keyCode() const { + if (m_keyCode) + return m_keyCode.value(); + // IE: virtual key code for keyup/keydown, character code for keypress // Firefox: virtual key code for keyup/keydown, zero for keypress // We match IE. @@ -173,6 +204,9 @@ int KeyboardEvent::keyCode() const int KeyboardEvent::charCode() const { + if (m_charCode) + return m_charCode.value(); + // IE: not supported // Firefox: 0 for keydown/keyup events, character code for keypress // We match Firefox, unless in backward compatibility mode, where we always return the character code. @@ -200,15 +234,17 @@ int KeyboardEvent::which() const { // Netscape's "which" returns a virtual key code for keydown and keyup, and a character code for keypress. // That's exactly what IE's "keyCode" returns. So they are the same for keyboard events. + if (m_which) + return m_which.value(); return keyCode(); } KeyboardEvent* findKeyboardEvent(Event* event) { for (Event* e = event; e; e = e->underlyingEvent()) - if (e->isKeyboardEvent()) - return static_cast<KeyboardEvent*>(e); - return 0; + if (is<KeyboardEvent>(*e)) + return downcast<KeyboardEvent>(e); + return nullptr; } } // namespace WebCore diff --git a/Source/WebCore/dom/KeyboardEvent.h b/Source/WebCore/dom/KeyboardEvent.h index b82a014c2..d17b5f6c9 100644 --- a/Source/WebCore/dom/KeyboardEvent.h +++ b/Source/WebCore/dom/KeyboardEvent.h @@ -21,10 +21,12 @@ * */ -#ifndef KeyboardEvent_h -#define KeyboardEvent_h +#pragma once +#include "EventModifierInit.h" +#include "KeypressCommand.h" #include "UIEventWithKeyState.h" +#include <memory> #include <wtf/Vector.h> namespace WebCore { @@ -32,94 +34,116 @@ namespace WebCore { class Node; class PlatformKeyboardEvent; -#if PLATFORM(MAC) -struct KeypressCommand { - KeypressCommand() { } - explicit KeypressCommand(const String& commandName) : commandName(commandName) { ASSERT(isASCIILower(commandName[0U])); } - KeypressCommand(const String& commandName, const String& text) : commandName(commandName), text(text) { ASSERT(commandName == "insertText:"); } - - String commandName; // Actually, a selector name - it may have a trailing colon, and a name that can be different from an editor command name. - String text; -}; -#endif - -struct KeyboardEventInit : public UIEventInit { - KeyboardEventInit(); - - String keyIdentifier; - unsigned location; - bool ctrlKey; - bool altKey; - bool shiftKey; - bool metaKey; -}; - -class KeyboardEvent : public UIEventWithKeyState { +class KeyboardEvent final : public UIEventWithKeyState { public: enum KeyLocationCode { DOM_KEY_LOCATION_STANDARD = 0x00, DOM_KEY_LOCATION_LEFT = 0x01, DOM_KEY_LOCATION_RIGHT = 0x02, DOM_KEY_LOCATION_NUMPAD = 0x03 - // FIXME: The following values are not supported yet (crbug.com/265446) - // DOM_KEY_LOCATION_MOBILE = 0x04, - // DOM_KEY_LOCATION_JOYSTICK = 0x05 }; - - static PassRefPtr<KeyboardEvent> create() + + static Ref<KeyboardEvent> create(const PlatformKeyboardEvent& platformEvent, DOMWindow* view) { - return adoptRef(new KeyboardEvent); + return adoptRef(*new KeyboardEvent(platformEvent, view)); } - static PassRefPtr<KeyboardEvent> create(const PlatformKeyboardEvent& platformEvent, AbstractView* view) + static Ref<KeyboardEvent> createForBindings() { - return adoptRef(new KeyboardEvent(platformEvent, view)); + return adoptRef(*new KeyboardEvent); } - static PassRefPtr<KeyboardEvent> create(const AtomicString& type, const KeyboardEventInit& initializer) + struct Init : public EventModifierInit { + String key; + String code; + unsigned location; + bool repeat; + bool isComposing; + + // Legacy. + String keyIdentifier; + std::optional<unsigned> keyLocation; + unsigned charCode; + unsigned keyCode; + unsigned which; + }; + + static Ref<KeyboardEvent> create(const AtomicString& type, const Init& initializer, IsTrusted isTrusted = IsTrusted::No) { - return adoptRef(new KeyboardEvent(type, initializer)); + return adoptRef(*new KeyboardEvent(type, initializer, isTrusted)); + } + + // FIXME: This method should be get ride of in the future. + // DO NOT USE IT! + static Ref<KeyboardEvent> createForDummy() + { + return adoptRef(*new KeyboardEvent(WTF::HashTableDeletedValue)); } virtual ~KeyboardEvent(); - void initKeyboardEvent(const AtomicString& type, bool canBubble, bool cancelable, AbstractView*, + WEBCORE_EXPORT void initKeyboardEvent(const AtomicString& type, bool canBubble, bool cancelable, DOMWindow*, const String& keyIdentifier, unsigned location, bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, bool altGraphKey = false); +#if ENABLE(KEYBOARD_KEY_ATTRIBUTE) + const String& key() const { return m_key; } +#endif +#if ENABLE(KEYBOARD_CODE_ATTRIBUTE) + const String& code() const { return m_code; } +#endif + const String& keyIdentifier() const { return m_keyIdentifier; } unsigned location() const { return m_location; } + bool repeat() const { return m_repeat; } - bool getModifierState(const String& keyIdentifier) const; - - bool altGraphKey() const { return m_altGraphKey; } + WEBCORE_EXPORT bool getModifierState(const String& keyIdentifier) const; const PlatformKeyboardEvent* keyEvent() const { return m_keyEvent.get(); } - virtual int keyCode() const override; // key code for keydown and keyup, character for keypress - virtual int charCode() const override; // character code for keypress, 0 for keydown and keyup + WEBCORE_EXPORT int keyCode() const; // key code for keydown and keyup, character for keypress + WEBCORE_EXPORT int charCode() const; // character code for keypress, 0 for keydown and keyup + + EventInterface eventInterface() const final; + bool isKeyboardEvent() const final; + int which() const final; - virtual EventInterface eventInterface() const override; - virtual bool isKeyboardEvent() const override; - virtual int which() const override; + bool isComposing() const { return m_isComposing; } -#if PLATFORM(MAC) - // We only have this need to store keypress command info on the Mac. +#if PLATFORM(COCOA) + bool handledByInputMethod() const { return m_handledByInputMethod; } + const Vector<KeypressCommand>& keypressCommands() const { return m_keypressCommands; } + + // The non-const version is still needed for WebKit1, which doesn't construct a complete KeyboardEvent with interpreted commands yet. Vector<KeypressCommand>& keypressCommands() { return m_keypressCommands; } #endif private: - KeyboardEvent(); - KeyboardEvent(const PlatformKeyboardEvent&, AbstractView*); - KeyboardEvent(const AtomicString&, const KeyboardEventInit&); - - OwnPtr<PlatformKeyboardEvent> m_keyEvent; + WEBCORE_EXPORT KeyboardEvent(); + WEBCORE_EXPORT KeyboardEvent(const PlatformKeyboardEvent&, DOMWindow*); + KeyboardEvent(const AtomicString&, const Init&, IsTrusted); + // FIXME: This method should be get rid of in the future. + // DO NOT USE IT! + KeyboardEvent(WTF::HashTableDeletedValueType); + + std::unique_ptr<PlatformKeyboardEvent> m_keyEvent; +#if ENABLE(KEYBOARD_KEY_ATTRIBUTE) + String m_key; +#endif +#if ENABLE(KEYBOARD_CODE_ATTRIBUTE) + String m_code; +#endif String m_keyIdentifier; - unsigned m_location; - bool m_altGraphKey : 1; - -#if PLATFORM(MAC) + unsigned m_location { DOM_KEY_LOCATION_STANDARD }; + bool m_repeat { false }; + bool m_isComposing { false }; + std::optional<unsigned> m_charCode; + std::optional<unsigned> m_keyCode; + std::optional<unsigned> m_which; + +#if PLATFORM(COCOA) // Commands that were sent by AppKit when interpreting the event. Doesn't include input method commands. + bool m_handledByInputMethod { false }; Vector<KeypressCommand> m_keypressCommands; #endif }; @@ -128,4 +152,4 @@ KeyboardEvent* findKeyboardEvent(Event*); } // namespace WebCore -#endif // KeyboardEvent_h +SPECIALIZE_TYPE_TRAITS_EVENT(KeyboardEvent) diff --git a/Source/WebCore/dom/KeyboardEvent.idl b/Source/WebCore/dom/KeyboardEvent.idl index a42059a5c..757c07793 100644 --- a/Source/WebCore/dom/KeyboardEvent.idl +++ b/Source/WebCore/dom/KeyboardEvent.idl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006 Apple Inc. * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> * * This library is free software; you can redistribute it and/or @@ -19,97 +19,55 @@ */ [ - ConstructorConditional=DOM4_EVENTS_CONSTRUCTOR, - ConstructorTemplate=Event, + Constructor(DOMString type, optional KeyboardEventInit eventInitDict), ] interface KeyboardEvent : UIEvent { -#if !defined(LANGUAGE_JAVASCRIPT) || !LANGUAGE_JAVASCRIPT - // KeyLocationCode - const unsigned long KEY_LOCATION_STANDARD = 0x00; - const unsigned long KEY_LOCATION_LEFT = 0x01; - const unsigned long KEY_LOCATION_RIGHT = 0x02; - const unsigned long KEY_LOCATION_NUMPAD = 0x03; -#endif - -#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT const unsigned long DOM_KEY_LOCATION_STANDARD = 0x00; - const unsigned long DOM_KEY_LOCATION_LEFT = 0x01; - const unsigned long DOM_KEY_LOCATION_RIGHT = 0x02; - const unsigned long DOM_KEY_LOCATION_NUMPAD = 0x03; - // FIXME: The following constants are defined in the specification but - // not yet supported. - // const unsigned long DOM_KEY_LOCATION_MOBILE = 0x04; - // const unsigned long DOM_KEY_LOCATION_JOYSTICK = 0x05; -#endif - - [InitializedByEventConstructor] readonly attribute DOMString keyIdentifier; - [InitializedByEventConstructor] readonly attribute unsigned long location; - [InitializedByEventConstructor, ImplementedAs=location] readonly attribute unsigned long keyLocation; // Deprecated. - [InitializedByEventConstructor] readonly attribute boolean ctrlKey; - [InitializedByEventConstructor] readonly attribute boolean shiftKey; - [InitializedByEventConstructor] readonly attribute boolean altKey; - [InitializedByEventConstructor] readonly attribute boolean metaKey; - readonly attribute boolean altGraphKey; + const unsigned long DOM_KEY_LOCATION_LEFT = 0x01; + const unsigned long DOM_KEY_LOCATION_RIGHT = 0x02; + const unsigned long DOM_KEY_LOCATION_NUMPAD = 0x03; -#if !defined(LANGUAGE_JAVASCRIPT) || !LANGUAGE_JAVASCRIPT - boolean getModifierState([Default=Undefined] optional DOMString keyIdentifierArg); -#endif + [Conditional=KEYBOARD_KEY_ATTRIBUTE] readonly attribute DOMString key; + [Conditional=KEYBOARD_CODE_ATTRIBUTE] readonly attribute DOMString code; + readonly attribute unsigned long location; - // FIXME: this does not match the version in the DOM spec. - void initKeyboardEvent([Default=Undefined] optional DOMString type, - [Default=Undefined] optional boolean canBubble, - [Default=Undefined] optional boolean cancelable, - [Default=Undefined] optional DOMWindow view, - [Default=Undefined] optional DOMString keyIdentifier, - [Default=Undefined] optional unsigned long location, - [Default=Undefined] optional boolean ctrlKey, - [Default=Undefined] optional boolean altKey, - [Default=Undefined] optional boolean shiftKey, - [Default=Undefined] optional boolean metaKey, - [Default=Undefined] optional boolean altGraphKey); + readonly attribute boolean ctrlKey; + readonly attribute boolean shiftKey; + readonly attribute boolean altKey; + readonly attribute boolean metaKey; + readonly attribute boolean repeat; - // WebKit Extensions -#if (!defined(LANGUAGE_JAVASCRIPT) || !LANGUAGE_JAVASCRIPT) && (!defined(LANGUAGE_GOBJECT) || !LANGUAGE_GOBJECT) - readonly attribute long keyCode; - readonly attribute long charCode; - - void initKeyboardEvent([Default=Undefined] optional DOMString type, - [Default=Undefined] optional boolean canBubble, - [Default=Undefined] optional boolean cancelable, - [Default=Undefined] optional DOMWindow view, - [Default=Undefined] optional DOMString keyIdentifier, - [Default=Undefined] optional unsigned long location, - [Default=Undefined] optional boolean ctrlKey, - [Default=Undefined] optional boolean altKey, - [Default=Undefined] optional boolean shiftKey, - [Default=Undefined] optional boolean metaKey); -#endif + readonly attribute boolean isComposing; -#if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C - // For backward compatibility. - void initKeyboardEvent([Default=Undefined] optional DOMString type, - [Default=Undefined] optional boolean canBubble, - [Default=Undefined] optional boolean cancelable, - [Default=Undefined] optional DOMWindow view, - [Default=Undefined] optional DOMString keyIdentifier, - [Default=Undefined] optional unsigned long keyLocation, - [Default=Undefined] optional boolean ctrlKey, - [Default=Undefined] optional boolean altKey, - [Default=Undefined] optional boolean shiftKey, - [Default=Undefined] optional boolean metaKey, - [Default=Undefined] optional boolean altGraphKey); + boolean getModifierState(DOMString keyArg); - void initKeyboardEvent([Default=Undefined] optional DOMString type, - [Default=Undefined] optional boolean canBubble, - [Default=Undefined] optional boolean cancelable, - [Default=Undefined] optional DOMWindow view, - [Default=Undefined] optional DOMString keyIdentifier, - [Default=Undefined] optional unsigned long keyLocation, - [Default=Undefined] optional boolean ctrlKey, - [Default=Undefined] optional boolean altKey, - [Default=Undefined] optional boolean shiftKey, - [Default=Undefined] optional boolean metaKey); -#endif + // Everything below is legacy. + readonly attribute DOMString keyIdentifier; + [ImplementedAs=location] readonly attribute unsigned long keyLocation; + readonly attribute boolean altGraphKey; + readonly attribute unsigned long charCode; + readonly attribute unsigned long keyCode; + readonly attribute unsigned long which; + // FIXME: this does not match the version in the DOM spec. + // FIXME: Using "undefined" as default parameter value is wrong. + void initKeyboardEvent(optional DOMString type = "undefined", optional boolean canBubble = false, optional boolean cancelable = false, + optional DOMWindow? view = null, optional DOMString keyIdentifier = "undefined", optional unsigned long location = 0, + optional boolean ctrlKey = false, optional boolean altKey = false, optional boolean shiftKey = false, optional boolean metaKey = false, optional boolean altGraphKey = false); }; +dictionary KeyboardEventInit : EventModifierInit { + DOMString key = ""; + DOMString code = ""; + unsigned long location = 0; + boolean repeat = false; + boolean isComposing = false; + + // This members are not in the specification but are needed to initialize the corresponding legacy + // attributes we still support on KeyboardEvent for backward compatibility. + DOMString keyIdentifier = ""; + unsigned long keyLocation; + unsigned long charCode = 0; + unsigned long keyCode = 0; + unsigned long which = 0; +}; diff --git a/Source/WebCore/dom/LiveNodeList.cpp b/Source/WebCore/dom/LiveNodeList.cpp index 0a15793b9..169d6de33 100644 --- a/Source/WebCore/dom/LiveNodeList.cpp +++ b/Source/WebCore/dom/LiveNodeList.cpp @@ -2,7 +2,7 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2004, 2006, 2007, 2008, 2010, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2006-2008, 2010, 2013-2014 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -23,157 +23,31 @@ #include "config.h" #include "LiveNodeList.h" -#include "ClassNodeList.h" +#include "ClassCollection.h" #include "Element.h" #include "ElementTraversal.h" #include "HTMLCollection.h" -#include "TagNodeList.h" namespace WebCore { -ContainerNode& LiveNodeList::rootNode() const +LiveNodeList::LiveNodeList(ContainerNode& ownerNode, NodeListInvalidationType invalidationType) + : m_ownerNode(ownerNode) + , m_invalidationType(invalidationType) + , m_isRegisteredForInvalidationAtDocument(false) { - if (isRootedAtDocument() && ownerNode().inDocument()) - return ownerNode().document(); - - return ownerNode(); + ASSERT(m_invalidationType == static_cast<unsigned>(invalidationType)); } -template <class NodeListType> -inline bool isMatchingElement(const NodeListType*, Element*); - -template <> inline bool isMatchingElement(const LiveNodeList* nodeList, Element* element) +LiveNodeList::~LiveNodeList() { - return nodeList->nodeMatches(element); } -template <> inline bool isMatchingElement(const HTMLTagNodeList* nodeList, Element* element) -{ - return nodeList->nodeMatchesInlined(element); -} - -template <> inline bool isMatchingElement(const ClassNodeList* nodeList, Element* element) -{ - return nodeList->nodeMatchesInlined(element); -} - -ALWAYS_INLINE Element* LiveNodeList::iterateForPreviousElement(Element* current) const -{ - ContainerNode& rootNode = this->rootNode(); - for (; current; current = ElementTraversal::previous(current, &rootNode)) { - if (isMatchingElement(static_cast<const LiveNodeList*>(this), current)) - return current; - } - return 0; -} - -template <class NodeListType> -inline Element* firstMatchingElement(const NodeListType* nodeList, ContainerNode& root) -{ - Element* element = ElementTraversal::firstWithin(&root); - while (element && !isMatchingElement(nodeList, element)) - element = ElementTraversal::next(element, &root); - return element; -} - -template <class NodeListType> -inline Element* nextMatchingElement(const NodeListType* nodeList, Element* current, ContainerNode& root) -{ - do { - current = ElementTraversal::next(current, &root); - } while (current && !isMatchingElement(nodeList, current)); - return current; -} - -template <class NodeListType> -inline Element* traverseMatchingElementsForward(const NodeListType* nodeList, Element& current, unsigned count, unsigned& traversedCount, ContainerNode& root) -{ - Element* element = ¤t; - for (traversedCount = 0; traversedCount < count; ++traversedCount) { - element = nextMatchingElement(nodeList, element, root); - if (!element) - return nullptr; - } - return element; -} - -Element* LiveNodeList::collectionFirst() const -{ - auto& root = rootNode(); - if (type() == HTMLTagNodeListType) - return firstMatchingElement(static_cast<const HTMLTagNodeList*>(this), root); - if (type() == ClassNodeListType) - return firstMatchingElement(static_cast<const ClassNodeList*>(this), root); - return firstMatchingElement(static_cast<const LiveNodeList*>(this), root); -} - -Element* LiveNodeList::collectionLast() const -{ - // FIXME: This should be optimized similarly to the forward case. - return iterateForPreviousElement(ElementTraversal::lastWithin(&rootNode())); -} - -Element* LiveNodeList::collectionTraverseForward(Element& current, unsigned count, unsigned& traversedCount) const -{ - auto& root = rootNode(); - if (type() == HTMLTagNodeListType) - return traverseMatchingElementsForward(static_cast<const HTMLTagNodeList*>(this), current, count, traversedCount, root); - if (type() == ClassNodeListType) - return traverseMatchingElementsForward(static_cast<const ClassNodeList*>(this), current, count, traversedCount, root); - return traverseMatchingElementsForward(static_cast<const LiveNodeList*>(this), current, count, traversedCount, root); -} - -Element* LiveNodeList::collectionTraverseBackward(Element& current, unsigned count) const -{ - // FIXME: This should be optimized similarly to the forward case. - auto& root = rootNode(); - Element* element = ¤t; - for (; count && element ; --count) - element = iterateForPreviousElement(ElementTraversal::previous(element, &root)); - return element; -} - -unsigned LiveNodeList::length() const -{ - return m_indexCache.nodeCount(*this); -} - -Node* LiveNodeList::item(unsigned offset) const -{ - return m_indexCache.nodeAt(*this, offset); -} - -void LiveNodeList::invalidateCache() const -{ - m_indexCache.invalidate(); -} - -Node* LiveNodeList::namedItem(const AtomicString& elementId) const +ContainerNode& LiveNodeList::rootNode() const { - // FIXME: Why doesn't this look into the name attribute like HTMLCollection::namedItem does? - Node& rootNode = this->rootNode(); - - if (rootNode.inDocument()) { - Element* element = rootNode.treeScope().getElementById(elementId); - if (element && nodeMatches(element) && element->isDescendantOf(&rootNode)) - return element; - if (!element) - return 0; - // In the case of multiple nodes with the same name, just fall through. - } - - unsigned length = this->length(); - for (unsigned i = 0; i < length; i++) { - Node* node = item(i); - if (!node->isElementNode()) - continue; - Element* element = toElement(node); - // FIXME: This should probably be using getIdAttribute instead of idForStyleResolution. - if (element->hasID() && element->idForStyleResolution() == elementId) - return node; - } + if (isRootedAtDocument() && ownerNode().isConnected()) + return ownerNode().document(); - return 0; + return ownerNode(); } } // namespace WebCore diff --git a/Source/WebCore/dom/LiveNodeList.h b/Source/WebCore/dom/LiveNodeList.h index 6e8ee08e5..415d1909f 100644 --- a/Source/WebCore/dom/LiveNodeList.h +++ b/Source/WebCore/dom/LiveNodeList.h @@ -2,7 +2,7 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2004, 2006, 2007, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2006-2007, 2013-2014 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -21,99 +21,87 @@ * */ -#ifndef LiveNodeList_h -#define LiveNodeList_h +#pragma once #include "CollectionIndexCache.h" +#include "CollectionTraversal.h" #include "CollectionType.h" #include "Document.h" +#include "ElementDescendantIterator.h" #include "HTMLNames.h" #include "NodeList.h" #include <wtf/Forward.h> -#include <wtf/RefPtr.h> namespace WebCore { class Element; -enum NodeListRootType { - NodeListIsRootedAtNode, - NodeListIsRootedAtDocument -}; - static bool shouldInvalidateTypeOnAttributeChange(NodeListInvalidationType, const QualifiedName&); class LiveNodeList : public NodeList { public: - enum Type { - ClassNodeListType, - NameNodeListType, - TagNodeListType, - HTMLTagNodeListType, - RadioNodeListType, - LabelsNodeListType, - }; - - LiveNodeList(ContainerNode& ownerNode, Type type, NodeListInvalidationType invalidationType, NodeListRootType rootType = NodeListIsRootedAtNode) - : m_ownerNode(ownerNode) - , m_rootType(rootType) - , m_invalidationType(invalidationType) - , m_type(type) - { - ASSERT(m_rootType == static_cast<unsigned>(rootType)); - ASSERT(m_invalidationType == static_cast<unsigned>(invalidationType)); - ASSERT(m_type == static_cast<unsigned>(type)); + LiveNodeList(ContainerNode& ownerNode, NodeListInvalidationType); + virtual ~LiveNodeList(); - document().registerNodeList(*this); - } - virtual Node* namedItem(const AtomicString&) const override final; - virtual bool nodeMatches(Element*) const = 0; + virtual bool elementMatches(Element&) const = 0; + virtual bool isRootedAtDocument() const = 0; - virtual ~LiveNodeList() - { - document().unregisterNodeList(*this); - } - - // DOM API - virtual unsigned length() const override final; - virtual Node* item(unsigned offset) const override final; - - ALWAYS_INLINE bool isRootedAtDocument() const { return m_rootType == NodeListIsRootedAtDocument; } ALWAYS_INLINE NodeListInvalidationType invalidationType() const { return static_cast<NodeListInvalidationType>(m_invalidationType); } - ALWAYS_INLINE Type type() const { return static_cast<Type>(m_type); } - ContainerNode& ownerNode() const { return const_cast<ContainerNode&>(m_ownerNode.get()); } - ALWAYS_INLINE void invalidateCache(const QualifiedName* attrName) const + ContainerNode& ownerNode() const { return m_ownerNode; } + ALWAYS_INLINE void invalidateCacheForAttribute(const QualifiedName* attrName) const { if (!attrName || shouldInvalidateTypeOnAttributeChange(invalidationType(), *attrName)) - invalidateCache(); + invalidateCache(document()); } - void invalidateCache() const; + virtual void invalidateCache(Document&) const = 0; - // For CollectionIndexCache - Element* collectionFirst() const; - Element* collectionLast() const; - Element* collectionTraverseForward(Element&, unsigned count, unsigned& traversedCount) const; - Element* collectionTraverseBackward(Element&, unsigned count) const; - bool collectionCanTraverseBackward() const { return true; } + bool isRegisteredForInvalidationAtDocument() const { return m_isRegisteredForInvalidationAtDocument; } + void setRegisteredForInvalidationAtDocument(bool f) { m_isRegisteredForInvalidationAtDocument = f; } protected: Document& document() const { return m_ownerNode->document(); } - ContainerNode& rootNode() const; - - ALWAYS_INLINE NodeListRootType rootType() const { return static_cast<NodeListRootType>(m_rootType); } private: - virtual bool isLiveNodeList() const override { return true; } + bool isLiveNodeList() const final { return true; } - Element* iterateForPreviousElement(Element* current) const; + ContainerNode& rootNode() const; Ref<ContainerNode> m_ownerNode; - mutable CollectionIndexCache<LiveNodeList, Element> m_indexCache; + const unsigned m_invalidationType; + bool m_isRegisteredForInvalidationAtDocument; +}; + +template <class NodeListType> +class CachedLiveNodeList : public LiveNodeList { +public: + virtual ~CachedLiveNodeList(); + + unsigned length() const final { return m_indexCache.nodeCount(nodeList()); } + Element* item(unsigned offset) const override { return m_indexCache.nodeAt(nodeList(), offset); } + + // For CollectionIndexCache + ElementDescendantIterator collectionBegin() const { return CollectionTraversal<CollectionTraversalType::Descendants>::begin(nodeList(), rootNode()); } + ElementDescendantIterator collectionLast() const { return CollectionTraversal<CollectionTraversalType::Descendants>::last(nodeList(), rootNode()); } + ElementDescendantIterator collectionEnd() const { return ElementDescendantIterator(); } + void collectionTraverseForward(ElementDescendantIterator& current, unsigned count, unsigned& traversedCount) const { CollectionTraversal<CollectionTraversalType::Descendants>::traverseForward(nodeList(), current, count, traversedCount); } + void collectionTraverseBackward(ElementDescendantIterator& current, unsigned count) const { CollectionTraversal<CollectionTraversalType::Descendants>::traverseBackward(nodeList(), current, count); } + bool collectionCanTraverseBackward() const { return true; } + void willValidateIndexCache() const { document().registerNodeListForInvalidation(const_cast<CachedLiveNodeList<NodeListType>&>(*this)); } + + void invalidateCache(Document&) const final; + size_t memoryCost() const final { return m_indexCache.memoryCost(); } - const unsigned m_rootType : 2; - const unsigned m_invalidationType : 4; - const unsigned m_type : 5; +protected: + CachedLiveNodeList(ContainerNode& rootNode, NodeListInvalidationType); + +private: + NodeListType& nodeList() { return static_cast<NodeListType&>(*this); } + const NodeListType& nodeList() const { return static_cast<const NodeListType&>(*this); } + + ContainerNode& rootNode() const; + + mutable CollectionIndexCache<NodeListType, ElementDescendantIterator> m_indexCache; }; ALWAYS_INLINE bool shouldInvalidateTypeOnAttributeChange(NodeListInvalidationType type, const QualifiedName& attrName) @@ -125,8 +113,8 @@ ALWAYS_INLINE bool shouldInvalidateTypeOnAttributeChange(NodeListInvalidationTyp return attrName == HTMLNames::nameAttr; case InvalidateOnIdNameAttrChange: return attrName == HTMLNames::idAttr || attrName == HTMLNames::nameAttr; - case InvalidateOnForAttrChange: - return attrName == HTMLNames::forAttr; + case InvalidateOnForTypeAttrChange: + return attrName == HTMLNames::forAttr || attrName == HTMLNames::typeAttr; case InvalidateForFormControls: return attrName == HTMLNames::nameAttr || attrName == HTMLNames::idAttr || attrName == HTMLNames::forAttr || attrName == HTMLNames::formAttr || attrName == HTMLNames::typeAttr; @@ -140,6 +128,36 @@ ALWAYS_INLINE bool shouldInvalidateTypeOnAttributeChange(NodeListInvalidationTyp return false; } -} // namespace WebCore +template <class NodeListType> +CachedLiveNodeList<NodeListType>::CachedLiveNodeList(ContainerNode& ownerNode, NodeListInvalidationType invalidationType) + : LiveNodeList(ownerNode, invalidationType) + , m_indexCache(nodeList()) +{ +} + +template <class NodeListType> +CachedLiveNodeList<NodeListType>::~CachedLiveNodeList() +{ + if (m_indexCache.hasValidCache(nodeList())) + document().unregisterNodeListForInvalidation(*this); +} + +template <class NodeListType> +inline ContainerNode& CachedLiveNodeList<NodeListType>::rootNode() const +{ + if (nodeList().isRootedAtDocument() && ownerNode().isConnected()) + return ownerNode().document(); + + return ownerNode(); +} -#endif // LiveNodeList_h +template <class NodeListType> +void CachedLiveNodeList<NodeListType>::invalidateCache(Document& document) const +{ + if (!m_indexCache.hasValidCache(nodeList())) + return; + document.unregisterNodeListForInvalidation(const_cast<NodeListType&>(nodeList())); + m_indexCache.invalidate(nodeList()); +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/LoadableClassicScript.cpp b/Source/WebCore/dom/LoadableClassicScript.cpp new file mode 100644 index 000000000..771d42d15 --- /dev/null +++ b/Source/WebCore/dom/LoadableClassicScript.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2016 Apple, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "LoadableClassicScript.h" + +#include "ScriptElement.h" +#include "ScriptSourceCode.h" +#include <wtf/NeverDestroyed.h> +#include <wtf/text/StringImpl.h> + +namespace WebCore { + +Ref<LoadableClassicScript> LoadableClassicScript::create(const String& nonce, const String& crossOriginMode, const String& charset, const AtomicString& initiatorName, bool isInUserAgentShadowTree) +{ + return adoptRef(*new LoadableClassicScript(nonce, crossOriginMode, charset, initiatorName, isInUserAgentShadowTree)); +} + +LoadableClassicScript::~LoadableClassicScript() +{ + if (m_cachedScript) + m_cachedScript->removeClient(*this); +} + +bool LoadableClassicScript::isLoaded() const +{ + ASSERT(m_cachedScript); + return m_cachedScript->isLoaded(); +} + +std::optional<LoadableScript::Error> LoadableClassicScript::error() const +{ + ASSERT(m_cachedScript); + if (m_error) + return m_error; + + if (m_cachedScript->errorOccurred()) + return Error { ErrorType::CachedScript, std::nullopt }; + + return std::nullopt; +} + +bool LoadableClassicScript::wasCanceled() const +{ + ASSERT(m_cachedScript); + return m_cachedScript->wasCanceled(); +} + +void LoadableClassicScript::notifyFinished(CachedResource& resource) +{ + ASSERT(m_cachedScript); + if (resource.resourceError().isAccessControl()) { + static NeverDestroyed<String> consoleMessage(ASCIILiteral("Cross-origin script load denied by Cross-Origin Resource Sharing policy.")); + m_error = Error { + ErrorType::CrossOriginLoad, + ConsoleMessage { + MessageSource::JS, + MessageLevel::Error, + consoleMessage + } + }; + } + +#if ENABLE(NOSNIFF) + if (!m_error && !m_cachedScript->mimeTypeAllowedByNosniff()) { + m_error = Error { + ErrorType::Nosniff, + ConsoleMessage { + MessageSource::Security, + MessageLevel::Error, + makeString( + "Refused to execute script from '", m_cachedScript->url().stringCenterEllipsizedToLength(), + "' because its MIME type ('", m_cachedScript->mimeType(), "') is not executable, and strict MIME type checking is enabled.") + } + }; + } +#endif + + notifyClientFinished(); +} + +void LoadableClassicScript::execute(ScriptElement& scriptElement) +{ + ASSERT(!error()); + scriptElement.executeClassicScript(ScriptSourceCode(m_cachedScript.get(), JSC::SourceProviderSourceType::Program, *this)); +} + +bool LoadableClassicScript::load(Document& document, const URL& sourceURL) +{ + ASSERT(!m_cachedScript); + m_cachedScript = requestScriptWithCache(document, sourceURL, crossOriginMode()); + if (!m_cachedScript) + return false; + m_cachedScript->addClient(*this); + return true; +} + +} diff --git a/Source/WebCore/dom/LoadableClassicScript.h b/Source/WebCore/dom/LoadableClassicScript.h new file mode 100644 index 000000000..dfac664dc --- /dev/null +++ b/Source/WebCore/dom/LoadableClassicScript.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2016 Apple, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "CachedResourceClient.h" +#include "CachedResourceHandle.h" +#include "CachedScript.h" +#include "LoadableScript.h" +#include "LoadableScriptClient.h" +#include <wtf/TypeCasts.h> + +namespace WebCore { + +// A CachedResourceHandle alone does not prevent the underlying CachedResource +// from purging its data buffer. This class holds a client until this class is +// destroyed in order to guarantee that the data buffer will not be purged. +class LoadableClassicScript final : public LoadableScript, private CachedResourceClient { +public: + virtual ~LoadableClassicScript(); + + static Ref<LoadableClassicScript> create(const String& nonce, const String& crossOriginMode, const String& charset, const AtomicString& initiatorName, bool isInUserAgentShadowTree); + bool isLoaded() const final; + std::optional<Error> error() const final; + bool wasCanceled() const final; + + CachedScript& cachedScript() { return *m_cachedScript; } + + bool isClassicScript() const final { return true; } + bool isModuleScript() const final { return false; } + + void execute(ScriptElement&) final; + + bool load(Document&, const URL&); + +private: + LoadableClassicScript(const String& nonce, const String& crossOriginMode, const String& charset, const AtomicString& initiatorName, bool isInUserAgentShadowTree) + : LoadableScript(nonce, crossOriginMode, charset, initiatorName, isInUserAgentShadowTree) + { + } + + void notifyFinished(CachedResource&) final; + + CachedResourceHandle<CachedScript> m_cachedScript { }; + std::optional<Error> m_error { std::nullopt }; +}; + +} + +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::LoadableClassicScript) + static bool isType(const WebCore::LoadableScript& script) { return script.isClassicScript(); } +SPECIALIZE_TYPE_TRAITS_END() diff --git a/Source/WebCore/dom/LoadableModuleScript.cpp b/Source/WebCore/dom/LoadableModuleScript.cpp new file mode 100644 index 000000000..125759811 --- /dev/null +++ b/Source/WebCore/dom/LoadableModuleScript.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2016 Apple, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "LoadableModuleScript.h" + +#include "Document.h" +#include "Frame.h" +#include "ScriptController.h" +#include "ScriptElement.h" + +namespace WebCore { + +Ref<LoadableModuleScript> LoadableModuleScript::create(const String& nonce, const String& crossOriginMode, const String& charset, const AtomicString& initiatorName, bool isInUserAgentShadowTree) +{ + return adoptRef(*new LoadableModuleScript(nonce, crossOriginMode, charset, initiatorName, isInUserAgentShadowTree)); +} + +LoadableModuleScript::LoadableModuleScript(const String& nonce, const String& crossOriginMode, const String& charset, const AtomicString& initiatorName, bool isInUserAgentShadowTree) + : LoadableScript(nonce, crossOriginMode, charset, initiatorName, isInUserAgentShadowTree) +{ +} + +LoadableModuleScript::~LoadableModuleScript() +{ +} + +bool LoadableModuleScript::isLoaded() const +{ + return m_isLoaded; +} + +std::optional<LoadableScript::Error> LoadableModuleScript::error() const +{ + return m_error; +} + +bool LoadableModuleScript::wasCanceled() const +{ + return m_wasCanceled; +} + +void LoadableModuleScript::notifyLoadCompleted(UniquedStringImpl& moduleKey) +{ + m_moduleKey = &moduleKey; + m_isLoaded = true; + notifyClientFinished(); +} + +void LoadableModuleScript::notifyLoadFailed(LoadableScript::Error&& error) +{ + m_error = WTFMove(error); + m_isLoaded = true; + notifyClientFinished(); +} + +void LoadableModuleScript::notifyLoadWasCanceled() +{ + m_wasCanceled = true; + m_isLoaded = true; + notifyClientFinished(); +} + +void LoadableModuleScript::execute(ScriptElement& scriptElement) +{ + scriptElement.executeModuleScript(*this); +} + +void LoadableModuleScript::load(Document& document, const URL& rootURL) +{ + if (auto* frame = document.frame()) + frame->script().loadModuleScript(*this, rootURL.string()); +} + +void LoadableModuleScript::load(Document& document, const ScriptSourceCode& sourceCode) +{ + if (auto* frame = document.frame()) + frame->script().loadModuleScript(*this, sourceCode); +} + +} diff --git a/Source/WebCore/dom/LoadableModuleScript.h b/Source/WebCore/dom/LoadableModuleScript.h new file mode 100644 index 000000000..7c55fe710 --- /dev/null +++ b/Source/WebCore/dom/LoadableModuleScript.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2016 Apple, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "LoadableScript.h" +#include <wtf/TypeCasts.h> + +namespace WebCore { + +class ScriptSourceCode; + +class LoadableModuleScript final : public LoadableScript { +public: + virtual ~LoadableModuleScript(); + + static Ref<LoadableModuleScript> create(const String& nonce, const String& crossOriginMode, const String& charset, const AtomicString& initiatorName, bool isInUserAgentShadowTree); + + bool isLoaded() const final; + std::optional<Error> error() const final; + bool wasCanceled() const final; + + bool isClassicScript() const final { return false; } + bool isModuleScript() const final { return true; } + + void execute(ScriptElement&) final; + + void setError(Error&&); + + void load(Document&, const URL& rootURL); + void load(Document&, const ScriptSourceCode&); + + void notifyLoadCompleted(UniquedStringImpl&); + void notifyLoadFailed(LoadableScript::Error&&); + void notifyLoadWasCanceled(); + + UniquedStringImpl* moduleKey() const { return m_moduleKey.get(); } + +private: + LoadableModuleScript(const String& nonce, const String& crossOriginMode, const String& charset, const AtomicString& initiatorName, bool isInUserAgentShadowTree); + + RefPtr<UniquedStringImpl> m_moduleKey; + std::optional<LoadableScript::Error> m_error; + bool m_wasCanceled { false }; + bool m_isLoaded { false }; +}; + +} + +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::LoadableModuleScript) + static bool isType(const WebCore::LoadableScript& script) { return script.isModuleScript(); } +SPECIALIZE_TYPE_TRAITS_END() diff --git a/Source/WebCore/dom/LoadableScript.cpp b/Source/WebCore/dom/LoadableScript.cpp new file mode 100644 index 000000000..1bdde1eb5 --- /dev/null +++ b/Source/WebCore/dom/LoadableScript.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2016 Apple, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "LoadableScript.h" + +#include "CachedResourceLoader.h" +#include "CachedScript.h" +#include "ContentSecurityPolicy.h" +#include "Document.h" +#include "LoadableScriptClient.h" +#include "Settings.h" + +namespace WebCore { + +void LoadableScript::addClient(LoadableScriptClient& client) +{ + m_clients.add(&client); + if (isLoaded()) { + Ref<LoadableScript> protectedThis(*this); + client.notifyFinished(*this); + } +} + +void LoadableScript::removeClient(LoadableScriptClient& client) +{ + m_clients.remove(&client); +} + +void LoadableScript::notifyClientFinished() +{ + RefPtr<LoadableScript> protectedThis(this); + + Vector<LoadableScriptClient*> vector; + for (auto& pair : m_clients) + vector.append(pair.key); + for (auto& client : vector) + client->notifyFinished(*this); +} + +} diff --git a/Source/WebCore/dom/LoadableScript.h b/Source/WebCore/dom/LoadableScript.h new file mode 100644 index 000000000..5b53c4f9e --- /dev/null +++ b/Source/WebCore/dom/LoadableScript.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2016 Apple, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "ScriptElementCachedScriptFetcher.h" +#include <runtime/ConsoleTypes.h> +#include <wtf/HashCountedSet.h> +#include <wtf/RefCounted.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class LoadableScriptClient; +class ScriptElement; + +class LoadableScript : public ScriptElementCachedScriptFetcher { +public: + enum class ErrorType { + CachedScript, + CrossOriginLoad, + Nosniff, + }; + + struct ConsoleMessage { + MessageSource source; + MessageLevel level; + String message; + }; + + struct Error { + ErrorType type; + std::optional<ConsoleMessage> consoleMessage; + }; + + virtual ~LoadableScript() { } + + virtual bool isLoaded() const = 0; + virtual std::optional<Error> error() const = 0; + virtual bool wasCanceled() const = 0; + + virtual void execute(ScriptElement&) = 0; + + void addClient(LoadableScriptClient&); + void removeClient(LoadableScriptClient&); + +protected: + LoadableScript(const String& nonce, const String& crossOriginMode, const String& charset, const AtomicString& initiatorName, bool isInUserAgentShadowTree) + : ScriptElementCachedScriptFetcher(nonce, crossOriginMode, charset, initiatorName, isInUserAgentShadowTree) + { + } + + void notifyClientFinished(); + +private: + HashCountedSet<LoadableScriptClient*> m_clients; +}; + +} diff --git a/Source/WebCore/dom/EventFactory.h b/Source/WebCore/dom/LoadableScriptClient.h index adfcfb5c6..c04e9cffb 100644 --- a/Source/WebCore/dom/EventFactory.h +++ b/Source/WebCore/dom/LoadableScriptClient.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Google, Inc. All Rights Reserved. + * Copyright (C) 2016 Apple, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,21 +23,17 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef EventFactory_h -#define EventFactory_h - -#include <wtf/PassRefPtr.h> -#include <wtf/text/AtomicString.h> +#pragma once namespace WebCore { -class Event; +class LoadableScript; -class EventFactory { +class LoadableScriptClient { public: - static PassRefPtr<Event> create(const String& eventType); + virtual ~LoadableScriptClient() { } + + virtual void notifyFinished(LoadableScript&) = 0; }; } - -#endif diff --git a/Source/WebCore/dom/MessageChannel.cpp b/Source/WebCore/dom/MessageChannel.cpp index 04d64d8fc..3ced2eb5e 100644 --- a/Source/WebCore/dom/MessageChannel.cpp +++ b/Source/WebCore/dom/MessageChannel.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR diff --git a/Source/WebCore/dom/MessageChannel.h b/Source/WebCore/dom/MessageChannel.h index 5236dea3a..33779b63c 100644 --- a/Source/WebCore/dom/MessageChannel.h +++ b/Source/WebCore/dom/MessageChannel.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,10 +24,8 @@ * */ -#ifndef MessageChannel_h -#define MessageChannel_h +#pragma once -#include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> #include <wtf/RefPtr.h> @@ -38,7 +36,7 @@ namespace WebCore { class MessageChannel : public RefCounted<MessageChannel> { public: - static PassRefPtr<MessageChannel> create(ScriptExecutionContext& context) { return adoptRef(new MessageChannel(context)); } + static Ref<MessageChannel> create(ScriptExecutionContext& context) { return adoptRef(*new MessageChannel(context)); } ~MessageChannel(); MessagePort* port1() const { return m_port1.get(); } @@ -52,5 +50,3 @@ namespace WebCore { }; } // namespace WebCore - -#endif // MessageChannel_h diff --git a/Source/WebCore/dom/MessageChannel.idl b/Source/WebCore/dom/MessageChannel.idl index 6855406ba..285afc95a 100644 --- a/Source/WebCore/dom/MessageChannel.idl +++ b/Source/WebCore/dom/MessageChannel.idl @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -25,12 +25,11 @@ */ [ - GlobalContext=DOMWindow&WorkerGlobalScope, + Exposed=(Window,Worker), Conditional=CHANNEL_MESSAGING, Constructor, ConstructorCallWith=ScriptExecutionContext, JSCustomMarkFunction, - JSNoStaticTables, ImplementationLacksVTable ] interface MessageChannel { diff --git a/Source/WebCore/dom/MessageEvent.cpp b/Source/WebCore/dom/MessageEvent.cpp index 4388c9fec..7031833d7 100644 --- a/Source/WebCore/dom/MessageEvent.cpp +++ b/Source/WebCore/dom/MessageEvent.cpp @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -28,58 +28,51 @@ #include "config.h" #include "MessageEvent.h" -namespace WebCore { +#include "Blob.h" +#include "EventNames.h" +#include <runtime/JSCInlines.h> -static inline bool isValidSource(EventTarget* source) -{ - return !source || source->toDOMWindow() || source->isMessagePort(); -} +namespace WebCore { -MessageEventInit::MessageEventInit() -{ -} +using namespace JSC; -MessageEvent::MessageEvent() +inline MessageEvent::MessageEvent() : m_dataType(DataTypeScriptValue) { } -MessageEvent::MessageEvent(const AtomicString& type, const MessageEventInit& initializer) - : Event(type, initializer) +inline MessageEvent::MessageEvent(ExecState& state, const AtomicString& type, Init&& initializer, IsTrusted isTrusted) + : Event(type, initializer, isTrusted) , m_dataType(DataTypeScriptValue) - , m_dataAsScriptValue(initializer.data) + , m_dataAsScriptValue(state.vm(), initializer.data) , m_origin(initializer.origin) , m_lastEventId(initializer.lastEventId) - , m_source(isValidSource(initializer.source.get()) ? initializer.source : 0) - , m_ports(adoptPtr(new MessagePortArray(initializer.ports))) + , m_source(WTFMove(initializer.source)) + , m_ports(WTFMove(initializer.ports)) { } -MessageEvent::MessageEvent(const Deprecated::ScriptValue& data, const String& origin, const String& lastEventId, PassRefPtr<EventTarget> source, PassOwnPtr<MessagePortArray> ports) +inline MessageEvent::MessageEvent(RefPtr<SerializedScriptValue>&& data, const String& origin, const String& lastEventId, std::optional<MessageEventSource>&& source, Vector<RefPtr<MessagePort>>&& ports) : Event(eventNames().messageEvent, false, false) - , m_dataType(DataTypeScriptValue) - , m_dataAsScriptValue(data) + , m_dataType(DataTypeSerializedScriptValue) + , m_dataAsSerializedScriptValue(WTFMove(data)) , m_origin(origin) , m_lastEventId(lastEventId) - , m_source(source) - , m_ports(ports) + , m_source(WTFMove(source)) + , m_ports(WTFMove(ports)) { - ASSERT(isValidSource(m_source.get())); } -MessageEvent::MessageEvent(PassRefPtr<SerializedScriptValue> data, const String& origin, const String& lastEventId, PassRefPtr<EventTarget> source, PassOwnPtr<MessagePortArray> ports) - : Event(eventNames().messageEvent, false, false) +inline MessageEvent::MessageEvent(const AtomicString& type, RefPtr<SerializedScriptValue>&& data, const String& origin, const String& lastEventId) + : Event(type, false, false) , m_dataType(DataTypeSerializedScriptValue) - , m_dataAsSerializedScriptValue(data) + , m_dataAsSerializedScriptValue(WTFMove(data)) , m_origin(origin) , m_lastEventId(lastEventId) - , m_source(source) - , m_ports(ports) { - ASSERT(isValidSource(m_source.get())); } -MessageEvent::MessageEvent(const String& data, const String& origin) +inline MessageEvent::MessageEvent(const String& data, const String& origin) : Event(eventNames().messageEvent, false, false) , m_dataType(DataTypeString) , m_dataAsString(data) @@ -87,27 +80,62 @@ MessageEvent::MessageEvent(const String& data, const String& origin) { } -MessageEvent::MessageEvent(PassRefPtr<Blob> data, const String& origin) +inline MessageEvent::MessageEvent(Ref<Blob>&& data, const String& origin) : Event(eventNames().messageEvent, false, false) , m_dataType(DataTypeBlob) - , m_dataAsBlob(data) + , m_dataAsBlob(WTFMove(data)) , m_origin(origin) { } -MessageEvent::MessageEvent(PassRefPtr<ArrayBuffer> data, const String& origin) +inline MessageEvent::MessageEvent(Ref<ArrayBuffer>&& data, const String& origin) : Event(eventNames().messageEvent, false, false) , m_dataType(DataTypeArrayBuffer) - , m_dataAsArrayBuffer(data) + , m_dataAsArrayBuffer(WTFMove(data)) , m_origin(origin) { } +Ref<MessageEvent> MessageEvent::create(Vector<RefPtr<MessagePort>>&& ports, RefPtr<SerializedScriptValue>&& data, const String& origin, const String& lastEventId, std::optional<MessageEventSource>&& source) +{ + return adoptRef(*new MessageEvent(WTFMove(data), origin, lastEventId, WTFMove(source), WTFMove(ports))); +} + +Ref<MessageEvent> MessageEvent::create(const AtomicString& type, RefPtr<SerializedScriptValue>&& data, const String& origin, const String& lastEventId) +{ + return adoptRef(*new MessageEvent(type, WTFMove(data), origin, lastEventId)); +} + +Ref<MessageEvent> MessageEvent::create(const String& data, const String& origin) +{ + return adoptRef(*new MessageEvent(data, origin)); +} + +Ref<MessageEvent> MessageEvent::create(Ref<Blob>&& data, const String& origin) +{ + return adoptRef(*new MessageEvent(WTFMove(data), origin)); +} + +Ref<MessageEvent> MessageEvent::create(Ref<ArrayBuffer>&& data, const String& origin) +{ + return adoptRef(*new MessageEvent(WTFMove(data), origin)); +} + +Ref<MessageEvent> MessageEvent::createForBindings() +{ + return adoptRef(*new MessageEvent); +} + +Ref<MessageEvent> MessageEvent::create(ExecState& state, const AtomicString& type, Init&& initializer, IsTrusted isTrusted) +{ + return adoptRef(*new MessageEvent(state, type, WTFMove(initializer), isTrusted)); +} + MessageEvent::~MessageEvent() { } -void MessageEvent::initMessageEvent(const AtomicString& type, bool canBubble, bool cancelable, const Deprecated::ScriptValue& data, const String& origin, const String& lastEventId, DOMWindow* source, PassOwnPtr<MessagePortArray> ports) +void MessageEvent::initMessageEvent(ExecState& state, const AtomicString& type, bool canBubble, bool cancelable, JSValue data, const String& origin, const String& lastEventId, std::optional<MessageEventSource>&& source, Vector<RefPtr<MessagePort>>&& ports) { if (dispatched()) return; @@ -115,26 +143,36 @@ void MessageEvent::initMessageEvent(const AtomicString& type, bool canBubble, bo initEvent(type, canBubble, cancelable); m_dataType = DataTypeScriptValue; - m_dataAsScriptValue = data; + m_dataAsScriptValue = Deprecated::ScriptValue(state.vm(), data); + m_dataAsSerializedScriptValue = nullptr; + m_triedToSerialize = false; m_origin = origin; m_lastEventId = lastEventId; - m_source = source; - m_ports = ports; + m_source = WTFMove(source); + m_ports = WTFMove(ports); } -void MessageEvent::initMessageEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<SerializedScriptValue> data, const String& origin, const String& lastEventId, DOMWindow* source, PassOwnPtr<MessagePortArray> ports) +EventTarget* MessageEvent::source() const { - if (dispatched()) - return; + if (!m_source) + return nullptr; - initEvent(type, canBubble, cancelable); + return WTF::switchOn(m_source.value(), + [] (const RefPtr<DOMWindow>& window) -> EventTarget* { return const_cast<DOMWindow*>(window.get()); }, + [] (const RefPtr<MessagePort>& messagePort) -> EventTarget* { return const_cast<MessagePort*>(messagePort.get()); } + ); +} - m_dataType = DataTypeSerializedScriptValue; - m_dataAsSerializedScriptValue = data; - m_origin = origin; - m_lastEventId = lastEventId; - m_source = source; - m_ports = ports; +RefPtr<SerializedScriptValue> MessageEvent::trySerializeData(ExecState* exec) +{ + ASSERT(!m_dataAsScriptValue.hasNoValue()); + + if (!m_dataAsSerializedScriptValue && !m_triedToSerialize) { + m_dataAsSerializedScriptValue = SerializedScriptValue::create(*exec, m_dataAsScriptValue.jsValue(), SerializationErrorMode::NonThrowing); + m_triedToSerialize = true; + } + + return m_dataAsSerializedScriptValue; } // FIXME: Remove this when we have custom ObjC binding support. @@ -145,24 +183,6 @@ SerializedScriptValue* MessageEvent::data() const return m_dataAsSerializedScriptValue.get(); } -MessagePort* MessageEvent::messagePort() -{ - if (!m_ports) - return 0; - ASSERT(m_ports->size() == 1); - return (*m_ports)[0].get(); -} - -void MessageEvent::initMessageEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<SerializedScriptValue> data, const String& origin, const String& lastEventId, DOMWindow* source, MessagePort* port) -{ - OwnPtr<MessagePortArray> ports; - if (port) { - ports = adoptPtr(new MessagePortArray); - ports->append(port); - } - initMessageEvent(type, canBubble, cancelable, data, origin, lastEventId, source, ports.release()); -} - EventInterface MessageEvent::eventInterface() const { return MessageEventInterfaceType; diff --git a/Source/WebCore/dom/MessageEvent.h b/Source/WebCore/dom/MessageEvent.h index cfb2fa414..efd65b3c4 100644 --- a/Source/WebCore/dom/MessageEvent.h +++ b/Source/WebCore/dom/MessageEvent.h @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -25,80 +25,51 @@ * */ -#ifndef MessageEvent_h -#define MessageEvent_h +#pragma once -#include "Blob.h" #include "DOMWindow.h" #include "Event.h" #include "MessagePort.h" #include "SerializedScriptValue.h" #include <bindings/ScriptValue.h> -#include <runtime/ArrayBuffer.h> +#include <wtf/Variant.h> namespace WebCore { -class EventTarget; +class Blob; -struct MessageEventInit : public EventInit { - MessageEventInit(); +using MessageEventSource = Variant<RefPtr<DOMWindow>, RefPtr<MessagePort>>; - Deprecated::ScriptValue data; - String origin; - String lastEventId; - RefPtr<EventTarget> source; - MessagePortArray ports; -}; - -class MessageEvent : public Event { +class MessageEvent final : public Event { public: - static PassRefPtr<MessageEvent> create() - { - return adoptRef(new MessageEvent); - } - static PassRefPtr<MessageEvent> create(PassOwnPtr<MessagePortArray> ports, const Deprecated::ScriptValue& data = Deprecated::ScriptValue(), const String& origin = String(), const String& lastEventId = String(), PassRefPtr<EventTarget> source = 0) - { - return adoptRef(new MessageEvent(data, origin, lastEventId, source, ports)); - } - static PassRefPtr<MessageEvent> create(PassOwnPtr<MessagePortArray> ports, PassRefPtr<SerializedScriptValue> data, const String& origin = String(), const String& lastEventId = String(), PassRefPtr<EventTarget> source = 0) - { - return adoptRef(new MessageEvent(data, origin, lastEventId, source, ports)); - } - static PassRefPtr<MessageEvent> create(const String& data, const String& origin = String()) - { - return adoptRef(new MessageEvent(data, origin)); - } - static PassRefPtr<MessageEvent> create(PassRefPtr<Blob> data, const String& origin = String()) - { - return adoptRef(new MessageEvent(data, origin)); - } - static PassRefPtr<MessageEvent> create(PassRefPtr<ArrayBuffer> data, const String& origin = String()) - { - return adoptRef(new MessageEvent(data, origin)); - } - static PassRefPtr<MessageEvent> create(const AtomicString& type, const MessageEventInit& initializer) - { - return adoptRef(new MessageEvent(type, initializer)); - } + static Ref<MessageEvent> create(Vector<RefPtr<MessagePort>>&&, RefPtr<SerializedScriptValue>&&, const String& origin = { }, const String& lastEventId = { }, std::optional<MessageEventSource>&& source = std::nullopt); + static Ref<MessageEvent> create(const AtomicString& type, RefPtr<SerializedScriptValue>&&, const String& origin, const String& lastEventId); + static Ref<MessageEvent> create(const String& data, const String& origin = { }); + static Ref<MessageEvent> create(Ref<Blob>&& data, const String& origin); + static Ref<MessageEvent> create(Ref<ArrayBuffer>&& data, const String& origin = { }); + static Ref<MessageEvent> createForBindings(); + + struct Init : EventInit { + JSC::JSValue data; + String origin; + String lastEventId; + std::optional<MessageEventSource> source; + Vector<RefPtr<MessagePort>> ports; + }; + static Ref<MessageEvent> create(JSC::ExecState&, const AtomicString& type, Init&&, IsTrusted = IsTrusted::No); + virtual ~MessageEvent(); - void initMessageEvent(const AtomicString& type, bool canBubble, bool cancelable, const Deprecated::ScriptValue& data, const String& origin, const String& lastEventId, DOMWindow* source, PassOwnPtr<MessagePortArray>); - void initMessageEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<SerializedScriptValue> data, const String& origin, const String& lastEventId, DOMWindow* source, PassOwnPtr<MessagePortArray>); + void initMessageEvent(JSC::ExecState&, const AtomicString& type, bool canBubble, bool cancelable, JSC::JSValue data, const String& origin, const String& lastEventId, std::optional<MessageEventSource>&&, Vector<RefPtr<MessagePort>>&&); const String& origin() const { return m_origin; } const String& lastEventId() const { return m_lastEventId; } - EventTarget* source() const { return m_source.get(); } - MessagePortArray ports() const { return m_ports ? *m_ports : MessagePortArray(); } + EventTarget* source() const; + const Vector<RefPtr<MessagePort>>& ports() const { return m_ports; } // FIXME: Remove this when we have custom ObjC binding support. SerializedScriptValue* data() const; - // Needed for Objective-C bindings (see bug 28774). - MessagePort* messagePort(); - void initMessageEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<SerializedScriptValue> data, const String& origin, const String& lastEventId, DOMWindow* source, MessagePort*); - - virtual EventInterface eventInterface() const override; - enum DataType { DataTypeScriptValue, DataTypeSerializedScriptValue, @@ -107,34 +78,36 @@ public: DataTypeArrayBuffer }; DataType dataType() const { return m_dataType; } - const Deprecated::ScriptValue& dataAsScriptValue() const { ASSERT(m_dataType == DataTypeScriptValue); return m_dataAsScriptValue; } - PassRefPtr<SerializedScriptValue> dataAsSerializedScriptValue() const { ASSERT(m_dataType == DataTypeSerializedScriptValue); return m_dataAsSerializedScriptValue; } + JSC::JSValue dataAsScriptValue() const { ASSERT(m_dataType == DataTypeScriptValue); return m_dataAsScriptValue; } + SerializedScriptValue* dataAsSerializedScriptValue() const { ASSERT(m_dataType == DataTypeSerializedScriptValue); return m_dataAsSerializedScriptValue.get(); } String dataAsString() const { ASSERT(m_dataType == DataTypeString); return m_dataAsString; } Blob* dataAsBlob() const { ASSERT(m_dataType == DataTypeBlob); return m_dataAsBlob.get(); } ArrayBuffer* dataAsArrayBuffer() const { ASSERT(m_dataType == DataTypeArrayBuffer); return m_dataAsArrayBuffer.get(); } + RefPtr<SerializedScriptValue> trySerializeData(JSC::ExecState*); + private: MessageEvent(); - MessageEvent(const AtomicString&, const MessageEventInit&); - MessageEvent(const Deprecated::ScriptValue& data, const String& origin, const String& lastEventId, PassRefPtr<EventTarget> source, PassOwnPtr<MessagePortArray>); - MessageEvent(PassRefPtr<SerializedScriptValue> data, const String& origin, const String& lastEventId, PassRefPtr<EventTarget> source, PassOwnPtr<MessagePortArray>); + MessageEvent(JSC::ExecState&, const AtomicString&, Init&&, IsTrusted); + MessageEvent(RefPtr<SerializedScriptValue>&& data, const String& origin, const String& lastEventId, std::optional<MessageEventSource>&&, Vector<RefPtr<MessagePort>>&&); + MessageEvent(const AtomicString& type, RefPtr<SerializedScriptValue>&& data, const String& origin, const String& lastEventId); + MessageEvent(const String& data, const String& origin); + MessageEvent(Ref<Blob>&& data, const String& origin); + MessageEvent(Ref<ArrayBuffer>&& data, const String& origin); - explicit MessageEvent(const String& data, const String& origin); - explicit MessageEvent(PassRefPtr<Blob> data, const String& origin); - explicit MessageEvent(PassRefPtr<ArrayBuffer> data, const String& origin); + EventInterface eventInterface() const final; DataType m_dataType; Deprecated::ScriptValue m_dataAsScriptValue; RefPtr<SerializedScriptValue> m_dataAsSerializedScriptValue; + bool m_triedToSerialize { false }; String m_dataAsString; RefPtr<Blob> m_dataAsBlob; RefPtr<ArrayBuffer> m_dataAsArrayBuffer; String m_origin; String m_lastEventId; - RefPtr<EventTarget> m_source; - OwnPtr<MessagePortArray> m_ports; + std::optional<MessageEventSource> m_source; + Vector<RefPtr<MessagePort>> m_ports; }; } // namespace WebCore - -#endif // MessageEvent_h diff --git a/Source/WebCore/dom/MessageEvent.idl b/Source/WebCore/dom/MessageEvent.idl index b678c2b95..ee696b9be 100644 --- a/Source/WebCore/dom/MessageEvent.idl +++ b/Source/WebCore/dom/MessageEvent.idl @@ -1,6 +1,7 @@ /* * Copyright (C) 2007 Henry Mason <hmason@mac.com> * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -11,10 +12,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,51 +27,28 @@ */ [ - GlobalContext=DOMWindow&WorkerGlobalScope, - JSNoStaticTables, - ConstructorTemplate=Event, + Constructor(DOMString type, optional MessageEventInit eventInitDict), + ConstructorCallWith=ScriptState, + Exposed=(Window,Worker), ] interface MessageEvent : Event { - [InitializedByEventConstructor] readonly attribute DOMString origin; - [InitializedByEventConstructor] readonly attribute DOMString lastEventId; - [InitializedByEventConstructor] readonly attribute EventTarget source; // May be a DOMWindow or a MessagePort. -#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT - [InitializedByEventConstructor, CachedAttribute, CustomGetter] readonly attribute any data; - [InitializedByEventConstructor] readonly attribute MessagePort[] ports; - - [Custom] void initMessageEvent([Default=Undefined] optional DOMString typeArg, - [Default=Undefined] optional boolean canBubbleArg, - [Default=Undefined] optional boolean cancelableArg, - [Default=Undefined] optional any dataArg, - [Default=Undefined] optional DOMString originArg, - [Default=Undefined] optional DOMString lastEventIdArg, - [Default=Undefined] optional DOMWindow sourceArg, - [Default=Undefined] optional Array messagePorts); - - [Custom] void webkitInitMessageEvent([Default=Undefined] optional DOMString typeArg, - [Default=Undefined] optional boolean canBubbleArg, - [Default=Undefined] optional boolean cancelableArg, - [Default=Undefined] optional any dataArg, - [Default=Undefined] optional DOMString originArg, - [Default=Undefined] optional DOMString lastEventIdArg, - [Default=Undefined] optional DOMWindow sourceArg, - [Default=Undefined] optional Array transferables); -#else - // Code generator for ObjC bindings does not support custom bindings, thus there is no good way to - // return a variant value. As workaround, expose the data attribute as SerializedScriptValue. - readonly attribute SerializedScriptValue data; - - // There's no good way to expose an array via the ObjC bindings, so for now just expose a single port. - readonly attribute MessagePort messagePort; - - void initMessageEvent([Default=Undefined] optional DOMString typeArg, - [Default=Undefined] optional boolean canBubbleArg, - [Default=Undefined] optional boolean cancelableArg, - [Default=Undefined] optional SerializedScriptValue dataArg, - [Default=Undefined] optional DOMString originArg, - [Default=Undefined] optional DOMString lastEventIdArg, - [Default=Undefined] optional DOMWindow sourceArg, - [Default=Undefined] optional MessagePort messagePort); -#endif + readonly attribute USVString origin; + readonly attribute DOMString lastEventId; + readonly attribute EventTarget? source; + [CachedAttribute, CustomGetter] readonly attribute any data; + readonly attribute FrozenArray<MessagePort> ports; + [CallWith=ScriptState] void initMessageEvent(DOMString type, optional boolean bubbles = false, optional boolean cancelable = false, + optional any data = null, optional USVString originArg = "", optional DOMString lastEventId = "", optional (DOMWindow or MessagePort)? source = null, + optional sequence<MessagePort> messagePorts = []); + [Custom] void webkitInitMessageEvent(optional DOMString typeArg, optional boolean canBubbleArg, optional boolean cancelableArg, + optional any dataArg, optional USVString originArg, optional DOMString lastEventIdArg, optional (DOMWindow or MessagePort)? sourceArg, + optional sequence<MessagePort> messagePorts); }; +dictionary MessageEventInit : EventInit { + any data = null; + USVString origin = ""; + DOMString lastEventId = ""; + (DOMWindow or MessagePort)? source = null; + sequence<MessagePort> ports = []; +}; diff --git a/Source/WebCore/dom/MessagePort.cpp b/Source/WebCore/dom/MessagePort.cpp index 64167230f..a44af43a2 100644 --- a/Source/WebCore/dom/MessagePort.cpp +++ b/Source/WebCore/dom/MessagePort.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -28,19 +28,17 @@ #include "MessagePort.h" #include "Document.h" -#include "EventException.h" +#include "EventNames.h" +#include "ExceptionCode.h" #include "MessageEvent.h" -#include "SecurityOrigin.h" #include "WorkerGlobalScope.h" namespace WebCore { MessagePort::MessagePort(ScriptExecutionContext& scriptExecutionContext) - : m_started(false) - , m_closed(false) - , m_scriptExecutionContext(&scriptExecutionContext) + : m_scriptExecutionContext(&scriptExecutionContext) { - m_scriptExecutionContext->createdMessagePort(this); + m_scriptExecutionContext->createdMessagePort(*this); // Don't need to call processMessagePortMessagesSoon() here, because the port will not be opened until start() is invoked. } @@ -49,52 +47,48 @@ MessagePort::~MessagePort() { close(); if (m_scriptExecutionContext) - m_scriptExecutionContext->destroyedMessagePort(this); + m_scriptExecutionContext->destroyedMessagePort(*this); } -void MessagePort::postMessage(PassRefPtr<SerializedScriptValue> message, MessagePort* port, ExceptionCode& ec) +ExceptionOr<void> MessagePort::postMessage(JSC::ExecState& state, JSC::JSValue messageValue, Vector<JSC::Strong<JSC::JSObject>>&& transfer) { - MessagePortArray ports; - if (port) - ports.append(port); - postMessage(message, &ports, ec); -} + Vector<RefPtr<MessagePort>> ports; + auto message = SerializedScriptValue::create(state, messageValue, WTFMove(transfer), ports); + if (message.hasException()) + return message.releaseException(); -void MessagePort::postMessage(PassRefPtr<SerializedScriptValue> message, const MessagePortArray* ports, ExceptionCode& ec) -{ if (!isEntangled()) - return; + return { }; ASSERT(m_scriptExecutionContext); - OwnPtr<MessagePortChannelArray> channels; + std::unique_ptr<MessagePortChannelArray> channels; // Make sure we aren't connected to any of the passed-in ports. - if (ports) { - for (unsigned int i = 0; i < ports->size(); ++i) { - MessagePort* dataPort = (*ports)[i].get(); - if (dataPort == this || m_entangledChannel->isConnectedTo(dataPort)) { - ec = DATA_CLONE_ERR; - return; - } + if (!ports.isEmpty()) { + for (auto& dataPort : ports) { + if (dataPort == this || m_entangledChannel->isConnectedTo(dataPort.get())) + return Exception { DATA_CLONE_ERR }; } - channels = MessagePort::disentanglePorts(ports, ec); - if (ec) - return; + auto disentangleResult = MessagePort::disentanglePorts(WTFMove(ports)); + if (disentangleResult.hasException()) + return disentangleResult.releaseException(); + channels = disentangleResult.releaseReturnValue(); } - m_entangledChannel->postMessageToRemote(message, channels.release()); + m_entangledChannel->postMessageToRemote(message.releaseReturnValue(), WTFMove(channels)); + return { }; } -PassOwnPtr<MessagePortChannel> MessagePort::disentangle() +std::unique_ptr<MessagePortChannel> MessagePort::disentangle() { ASSERT(m_entangledChannel); m_entangledChannel->disentangle(); - // We can't receive any messages or generate any events, so remove ourselves from the list of active ports. + // We can't receive any messages or generate any events after this, so remove ourselves from the list of active ports. ASSERT(m_scriptExecutionContext); - m_scriptExecutionContext->destroyedMessagePort(this); - m_scriptExecutionContext = 0; + m_scriptExecutionContext->destroyedMessagePort(*this); + m_scriptExecutionContext = nullptr; - return m_entangledChannel.release(); + return WTFMove(m_entangledChannel); } // Invoked to notify us that there are messages available for this port. @@ -126,7 +120,7 @@ void MessagePort::close() m_closed = true; } -void MessagePort::entangle(PassOwnPtr<MessagePortChannel> remote) +void MessagePort::entangle(std::unique_ptr<MessagePortChannel>&& remote) { // Only invoked to set our initial entanglement. ASSERT(!m_entangledChannel); @@ -134,7 +128,7 @@ void MessagePort::entangle(PassOwnPtr<MessagePortChannel> remote) // Don't entangle the ports if the channel is closed. if (remote->entangleIfOpen(this)) - m_entangledChannel = remote; + m_entangledChannel = WTFMove(remote); } void MessagePort::contextDestroyed() @@ -143,7 +137,7 @@ void MessagePort::contextDestroyed() // Must be closed before blowing away the cached context, to ensure that we get no more calls to messageAvailable(). // ScriptExecutionContext::closeMessagePorts() takes care of that. ASSERT(m_closed); - m_scriptExecutionContext = 0; + m_scriptExecutionContext = nullptr; } void MessagePort::dispatchMessages() @@ -152,18 +146,19 @@ void MessagePort::dispatchMessages() // The HTML5 spec specifies that any messages sent to a document that is not fully active should be dropped, so this behavior is OK. ASSERT(started()); - RefPtr<SerializedScriptValue> message; - OwnPtr<MessagePortChannelArray> channels; - while (m_entangledChannel && m_entangledChannel->tryGetMessageFromRemote(message, channels)) { + if (!m_entangledChannel) + return; + + bool contextIsWorker = is<WorkerGlobalScope>(*m_scriptExecutionContext); + auto pendingMessages = m_entangledChannel->takeAllMessagesFromRemote(); + for (auto& message : pendingMessages) { // close() in Worker onmessage handler should prevent next message from dispatching. - if (m_scriptExecutionContext->isWorkerGlobalScope() && static_cast<WorkerGlobalScope*>(m_scriptExecutionContext)->isClosing()) + if (contextIsWorker && downcast<WorkerGlobalScope>(*m_scriptExecutionContext).isClosing()) return; - OwnPtr<MessagePortArray> ports = MessagePort::entanglePorts(*m_scriptExecutionContext, channels.release()); - RefPtr<Event> evt = MessageEvent::create(ports.release(), message.release()); - - dispatchEvent(evt.release(), ASSERT_NO_EXCEPTION); + auto ports = MessagePort::entanglePorts(*m_scriptExecutionContext, WTFMove(message->channels)); + dispatchEvent(MessageEvent::create(WTFMove(ports), WTFMove(message->message))); } } @@ -180,48 +175,48 @@ bool MessagePort::hasPendingActivity() MessagePort* MessagePort::locallyEntangledPort() { - return m_entangledChannel ? m_entangledChannel->locallyEntangledPort(m_scriptExecutionContext) : 0; + return m_entangledChannel ? m_entangledChannel->locallyEntangledPort(m_scriptExecutionContext) : nullptr; } -PassOwnPtr<MessagePortChannelArray> MessagePort::disentanglePorts(const MessagePortArray* ports, ExceptionCode& ec) +ExceptionOr<std::unique_ptr<MessagePortChannelArray>> MessagePort::disentanglePorts(Vector<RefPtr<MessagePort>>&& ports) { - if (!ports || !ports->size()) + if (ports.isEmpty()) return nullptr; - // HashSet used to efficiently check for duplicates in the passed-in array. - HashSet<MessagePort*> portSet; - // Walk the incoming array - if there are any duplicate ports, or null ports or cloned ports, throw an error (per section 8.3.3 of the HTML5 spec). - for (unsigned int i = 0; i < ports->size(); ++i) { - MessagePort* port = (*ports)[i].get(); - if (!port || port->isNeutered() || portSet.contains(port)) { - ec = DATA_CLONE_ERR; - return nullptr; - } - portSet.add(port); + HashSet<MessagePort*> portSet; + for (auto& port : ports) { + if (!port || port->isNeutered() || !portSet.add(port.get()).isNewEntry) + return Exception { DATA_CLONE_ERR }; } // Passed-in ports passed validity checks, so we can disentangle them. - OwnPtr<MessagePortChannelArray> portArray = adoptPtr(new MessagePortChannelArray(ports->size())); - for (unsigned int i = 0 ; i < ports->size() ; ++i) { - OwnPtr<MessagePortChannel> channel = (*ports)[i]->disentangle(); - (*portArray)[i] = channel.release(); - } - return portArray.release(); + auto portArray = std::make_unique<MessagePortChannelArray>(ports.size()); + for (unsigned i = 0 ; i < ports.size(); ++i) + (*portArray)[i] = ports[i]->disentangle(); + return WTFMove(portArray); } -PassOwnPtr<MessagePortArray> MessagePort::entanglePorts(ScriptExecutionContext& context, PassOwnPtr<MessagePortChannelArray> channels) +Vector<RefPtr<MessagePort>> MessagePort::entanglePorts(ScriptExecutionContext& context, std::unique_ptr<MessagePortChannelArray>&& channels) { if (!channels || !channels->size()) - return nullptr; + return { }; - OwnPtr<MessagePortArray> portArray = adoptPtr(new MessagePortArray(channels->size())); + Vector<RefPtr<MessagePort>> portArray; + portArray.reserveInitialCapacity(channels->size()); for (unsigned int i = 0; i < channels->size(); ++i) { - RefPtr<MessagePort> port = MessagePort::create(context); - port->entangle((*channels)[i].release()); - (*portArray)[i] = port.release(); + auto port = MessagePort::create(context); + port->entangle(WTFMove((*channels)[i])); + portArray.uncheckedAppend(WTFMove(port)); } - return portArray.release(); + return portArray; +} + +bool MessagePort::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options) +{ + if (listener->isAttribute() && eventType == eventNames().messageEvent) + start(); + return EventTargetWithInlineData::addEventListener(eventType, WTFMove(listener), options); } } // namespace WebCore diff --git a/Source/WebCore/dom/MessagePort.h b/Source/WebCore/dom/MessagePort.h index 9e043748b..1bf676d62 100644 --- a/Source/WebCore/dom/MessagePort.h +++ b/Source/WebCore/dom/MessagePort.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,98 +24,81 @@ * */ -#ifndef MessagePort_h -#define MessagePort_h +#pragma once -#include "EventListener.h" #include "EventTarget.h" +#include "ExceptionOr.h" #include "MessagePortChannel.h" -#include <wtf/Forward.h> -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> -#include <wtf/PassRefPtr.h> -#include <wtf/RefPtr.h> -#include <wtf/Vector.h> -namespace WebCore { +namespace JSC { +class ExecState; +class JSObject; +class JSValue; +} - class Event; - class Frame; - class MessagePort; - class ScriptExecutionContext; +namespace WebCore { - // The overwhelmingly common case is sending a single port, so handle that efficiently with an inline buffer of size 1. - typedef Vector<RefPtr<MessagePort>, 1> MessagePortArray; +class Frame; - class MessagePort final : public RefCounted<MessagePort>, public EventTargetWithInlineData { - public: - static PassRefPtr<MessagePort> create(ScriptExecutionContext& scriptExecutionContext) { return adoptRef(new MessagePort(scriptExecutionContext)); } - virtual ~MessagePort(); +class MessagePort final : public RefCounted<MessagePort>, public EventTargetWithInlineData { +public: + static Ref<MessagePort> create(ScriptExecutionContext& scriptExecutionContext) { return adoptRef(*new MessagePort(scriptExecutionContext)); } + virtual ~MessagePort(); - void postMessage(PassRefPtr<SerializedScriptValue> message, const MessagePortArray*, ExceptionCode&); - // Needed for Objective-C bindings (see bug 28774). - void postMessage(PassRefPtr<SerializedScriptValue> message, MessagePort*, ExceptionCode&); + ExceptionOr<void> postMessage(JSC::ExecState&, JSC::JSValue message, Vector<JSC::Strong<JSC::JSObject>>&&); - void start(); - void close(); + void start(); + void close(); - void entangle(PassOwnPtr<MessagePortChannel>); - PassOwnPtr<MessagePortChannel> disentangle(); + void entangle(std::unique_ptr<MessagePortChannel>&&); - // Returns 0 if there is an exception, or if the passed-in array is 0/empty. - static PassOwnPtr<MessagePortChannelArray> disentanglePorts(const MessagePortArray*, ExceptionCode&); + // Returns nullptr if the passed-in vector is empty. + static ExceptionOr<std::unique_ptr<MessagePortChannelArray>> disentanglePorts(Vector<RefPtr<MessagePort>>&&); - // Returns 0 if the passed array is 0/empty. - static PassOwnPtr<MessagePortArray> entanglePorts(ScriptExecutionContext&, PassOwnPtr<MessagePortChannelArray>); + static Vector<RefPtr<MessagePort>> entanglePorts(ScriptExecutionContext&, std::unique_ptr<MessagePortChannelArray>&&); - void messageAvailable(); - bool started() const { return m_started; } + void messageAvailable(); + bool started() const { return m_started; } - void contextDestroyed(); + void contextDestroyed(); - virtual EventTargetInterface eventTargetInterface() const override { return MessagePortEventTargetInterfaceType; } - virtual ScriptExecutionContext* scriptExecutionContext() const override { return m_scriptExecutionContext; } - virtual bool isMessagePort() const override { return true; } + ScriptExecutionContext* scriptExecutionContext() const final { return m_scriptExecutionContext; } - void dispatchMessages(); + void dispatchMessages(); - using RefCounted<MessagePort>::ref; - using RefCounted<MessagePort>::deref; + bool hasPendingActivity(); - bool hasPendingActivity(); + // Returns null if there is no entangled port, or if the entangled port is run by a different thread. + // This is used solely to enable a GC optimization. Some platforms may not be able to determine ownership + // of the remote port (since it may live cross-process) - those platforms may always return null. + MessagePort* locallyEntangledPort(); - void setOnmessage(PassRefPtr<EventListener> listener) - { - setAttributeEventListener(eventNames().messageEvent, listener); - start(); - } - EventListener* onmessage() { return getAttributeEventListener(eventNames().messageEvent); } + using RefCounted::ref; + using RefCounted::deref; - // Returns null if there is no entangled port, or if the entangled port is run by a different thread. - // This is used solely to enable a GC optimization. Some platforms may not be able to determine ownership - // of the remote port (since it may live cross-process) - those platforms may always return null. - MessagePort* locallyEntangledPort(); +private: + explicit MessagePort(ScriptExecutionContext&); - // A port starts out its life entangled, and remains entangled until it is closed or is cloned. - bool isEntangled() { return !m_closed && !isNeutered(); } + void refEventTarget() final { ref(); } + void derefEventTarget() final { deref(); } - // A port gets neutered when it is transferred to a new owner via postMessage(). - bool isNeutered() { return !m_entangledChannel; } + bool isMessagePort() const final { return true; } + EventTargetInterface eventTargetInterface() const final { return MessagePortEventTargetInterfaceType; } - private: - explicit MessagePort(ScriptExecutionContext&); + bool addEventListener(const AtomicString& eventType, Ref<EventListener>&&, const AddEventListenerOptions&) final; - virtual void refEventTarget() override { ref(); } - virtual void derefEventTarget() override { deref(); } + std::unique_ptr<MessagePortChannel> disentangle(); - OwnPtr<MessagePortChannel> m_entangledChannel; + // A port starts out its life entangled, and remains entangled until it is closed or is cloned. + bool isEntangled() const { return !m_closed && !isNeutered(); } - bool m_started; - bool m_closed; + // A port gets neutered when it is transferred to a new owner via postMessage(). + bool isNeutered() const { return !m_entangledChannel; } - ScriptExecutionContext* m_scriptExecutionContext; - }; + std::unique_ptr<MessagePortChannel> m_entangledChannel; + bool m_started { false }; + bool m_closed { false }; + ScriptExecutionContext* m_scriptExecutionContext; +}; } // namespace WebCore - -#endif // MessagePort_h diff --git a/Source/WebCore/dom/MessagePort.idl b/Source/WebCore/dom/MessagePort.idl index 704263fa9..468ff5dcb 100644 --- a/Source/WebCore/dom/MessagePort.idl +++ b/Source/WebCore/dom/MessagePort.idl @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,31 +26,14 @@ */ [ - JSCustomMarkFunction, - GenerateIsReachable=Impl, ActiveDOMObject, - EventTarget, - JSNoStaticTables, -] interface MessagePort { -// We need to have something as an ObjC binding, because MessagePort is used in MessageEvent, which already has one, -// but we don't want to actually expose the API while it is in flux. -#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT - [Custom, RaisesException] void postMessage(any message, optional Array messagePorts); - + Exposed=(Window,Worker), + GenerateIsReachable=Impl, + JSCustomMarkFunction, +] interface MessagePort : EventTarget { + [CallWith=ScriptState, MayThrowException] void postMessage(any message, optional sequence<object> transfer = []); void start(); void close(); - // event handler attributes - attribute EventListener onmessage; - - // EventTarget interface - void addEventListener(DOMString type, - EventListener listener, - optional boolean useCapture); - void removeEventListener(DOMString type, - EventListener listener, - optional boolean useCapture); - [RaisesException] boolean dispatchEvent(Event evt); -#endif + attribute EventHandler onmessage; }; - diff --git a/Source/WebCore/dom/MessagePortChannel.h b/Source/WebCore/dom/MessagePortChannel.h index 9e1b7cba6..60ed6f836 100644 --- a/Source/WebCore/dom/MessagePortChannel.h +++ b/Source/WebCore/dom/MessagePortChannel.h @@ -28,15 +28,12 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef MessagePortChannel_h -#define MessagePortChannel_h +#pragma once #include "SerializedScriptValue.h" -#include <wtf/OwnPtr.h> +#include <memory> +#include <wtf/Deque.h> #include <wtf/Forward.h> -#include <wtf/PassOwnPtr.h> -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> #include <wtf/RefPtr.h> #include <wtf/text/WTFString.h> @@ -46,17 +43,27 @@ namespace WebCore { class MessagePortChannel; class PlatformMessagePortChannel; class ScriptExecutionContext; - class SerializedScriptValue; // The overwhelmingly common case is sending a single port, so handle that efficiently with an inline buffer of size 1. - typedef Vector<OwnPtr<MessagePortChannel>, 1> MessagePortChannelArray; + typedef Vector<std::unique_ptr<MessagePortChannel>, 1> MessagePortChannelArray; // MessagePortChannel is a platform-independent interface to the remote side of a message channel. // It acts as a wrapper around the platform-dependent PlatformMessagePortChannel implementation which ensures that the platform-dependent close() method is invoked before destruction. class MessagePortChannel { WTF_MAKE_NONCOPYABLE(MessagePortChannel); WTF_MAKE_FAST_ALLOCATED; public: - static void createChannel(PassRefPtr<MessagePort>, PassRefPtr<MessagePort>); + struct EventData { + EventData(Ref<SerializedScriptValue>&& message, std::unique_ptr<MessagePortChannelArray>&& channels) + : message(WTFMove(message)) + , channels(WTFMove(channels)) + { } + + Ref<SerializedScriptValue> message; + std::unique_ptr<MessagePortChannelArray> channels; + }; + + explicit MessagePortChannel(RefPtr<PlatformMessagePortChannel>&&); + static void createChannel(MessagePort*, MessagePort*); // Entangles the channel with a port (called when a port has been cloned, after the clone has been marshaled to its new owning thread and is ready to receive messages). // Returns false if the entanglement failed because the port was closed. @@ -75,10 +82,12 @@ namespace WebCore { bool hasPendingActivity(); // Sends a message and optional cloned port to the remote port. - void postMessageToRemote(PassRefPtr<SerializedScriptValue>, PassOwnPtr<MessagePortChannelArray>); + void postMessageToRemote(Ref<SerializedScriptValue>&&, std::unique_ptr<MessagePortChannelArray>); // Extracts a message from the message queue for this port. - bool tryGetMessageFromRemote(RefPtr<SerializedScriptValue>&, OwnPtr<MessagePortChannelArray>&); + std::unique_ptr<EventData> takeMessageFromRemote(); + + Deque<std::unique_ptr<EventData>> takeAllMessagesFromRemote(); // Returns the entangled port if run by the same thread (see MessagePort::locallyEntangledPort() for more details). MessagePort* locallyEntangledPort(const ScriptExecutionContext*); @@ -86,10 +95,7 @@ namespace WebCore { ~MessagePortChannel(); private: - explicit MessagePortChannel(PassRefPtr<PlatformMessagePortChannel>); RefPtr<PlatformMessagePortChannel> m_channel; }; } // namespace WebCore - -#endif // MessagePortChannel_h diff --git a/Source/WebCore/dom/Microtasks.cpp b/Source/WebCore/dom/Microtasks.cpp new file mode 100644 index 000000000..e5a2c951e --- /dev/null +++ b/Source/WebCore/dom/Microtasks.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2014 Yoav Weiss (yoav@yoav.ws) + * Copyright (C) 2015 Akamai Technologies Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "Microtasks.h" + +#include <wtf/MainThread.h> +#include <wtf/SetForScope.h> + +namespace WebCore { + +void Microtask::removeSelfFromQueue(MicrotaskQueue& queue) +{ + queue.remove(*this); +} + +MicrotaskQueue::MicrotaskQueue() + : m_timer(*this, &MicrotaskQueue::timerFired) +{ +} + +MicrotaskQueue::~MicrotaskQueue() +{ +} + +MicrotaskQueue& MicrotaskQueue::mainThreadQueue() +{ + ASSERT(isMainThread()); + static NeverDestroyed<MicrotaskQueue> queue; + return queue; +} + +void MicrotaskQueue::append(std::unique_ptr<Microtask>&& task) +{ + m_microtaskQueue.append(WTFMove(task)); + + m_timer.startOneShot(0); +} + +void MicrotaskQueue::remove(const Microtask& task) +{ + for (size_t i = 0; i < m_microtaskQueue.size(); ++i) { + if (m_microtaskQueue[i].get() == &task) { + m_microtaskQueue.remove(i); + return; + } + } +} + +void MicrotaskQueue::timerFired() +{ + performMicrotaskCheckpoint(); +} + +void MicrotaskQueue::performMicrotaskCheckpoint() +{ + if (m_performingMicrotaskCheckpoint) + return; + + SetForScope<bool> change(m_performingMicrotaskCheckpoint, true); + + Vector<std::unique_ptr<Microtask>> toKeep; + while (!m_microtaskQueue.isEmpty()) { + Vector<std::unique_ptr<Microtask>> queue = WTFMove(m_microtaskQueue); + for (auto& task : queue) { + auto result = task->run(); + switch (result) { + case Microtask::Result::Done: + break; + case Microtask::Result::KeepInQueue: + toKeep.append(WTFMove(task)); + break; + } + } + } + + m_microtaskQueue = WTFMove(toKeep); +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/Microtasks.h b/Source/WebCore/dom/Microtasks.h new file mode 100644 index 000000000..c6a083868 --- /dev/null +++ b/Source/WebCore/dom/Microtasks.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2014 Yoav Weiss (yoav@yoav.ws) + * Copyright (C) 2015 Akamai Technologies Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#pragma once + +#include "Timer.h" +#include <wtf/NeverDestroyed.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class MicrotaskQueue; + +class Microtask { +public: + virtual ~Microtask() + { + } + + enum class Result { + Done, + KeepInQueue + }; + + virtual Result run() = 0; + +protected: + void removeSelfFromQueue(MicrotaskQueue&); +}; + +class MicrotaskQueue { + friend NeverDestroyed<MicrotaskQueue>; + friend class Microtask; +public: + WEBCORE_EXPORT static MicrotaskQueue& mainThreadQueue(); + + WEBCORE_EXPORT MicrotaskQueue(); + WEBCORE_EXPORT ~MicrotaskQueue(); + + WEBCORE_EXPORT void append(std::unique_ptr<Microtask>&&); + WEBCORE_EXPORT void performMicrotaskCheckpoint(); + +private: + WEBCORE_EXPORT void remove(const Microtask&); + + void timerFired(); + + bool m_performingMicrotaskCheckpoint = false; + Vector<std::unique_ptr<Microtask>> m_microtaskQueue; + + // FIXME: Instead of a Timer, we should have a centralized Event Loop that calls performMicrotaskCheckpoint() + // on every iteration, implementing https://html.spec.whatwg.org/multipage/webappapis.html#processing-model-9 + Timer m_timer; +}; + +} // namespace WebCore diff --git a/Source/WebCore/dom/MouseEvent.cpp b/Source/WebCore/dom/MouseEvent.cpp index 5682f8a53..b240a7dbf 100644 --- a/Source/WebCore/dom/MouseEvent.cpp +++ b/Source/WebCore/dom/MouseEvent.cpp @@ -2,7 +2,7 @@ * Copyright (C) 2001 Peter Kelly (pmk@post.com) * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - * Copyright (C) 2003, 2005, 2006, 2008, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2003-2016 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -23,7 +23,7 @@ #include "config.h" #include "MouseEvent.h" -#include "Clipboard.h" +#include "DataTransfer.h" #include "EventNames.h" #include "Frame.h" #include "FrameView.h" @@ -33,29 +33,13 @@ namespace WebCore { -MouseEventInit::MouseEventInit() - : screenX(0) - , screenY(0) - , clientX(0) - , clientY(0) - , ctrlKey(false) - , altKey(false) - , shiftKey(false) - , metaKey(false) - , button(0) - , relatedTarget(0) +Ref<MouseEvent> MouseEvent::create(const AtomicString& type, const MouseEventInit& initializer, IsTrusted isTrusted) { + return adoptRef(*new MouseEvent(type, initializer, isTrusted)); } -PassRefPtr<MouseEvent> MouseEvent::create(const AtomicString& type, const MouseEventInit& initializer) +Ref<MouseEvent> MouseEvent::create(const AtomicString& eventType, DOMWindow* view, const PlatformMouseEvent& event, int detail, Node* relatedTarget) { - return adoptRef(new MouseEvent(type, initializer)); -} - -PassRefPtr<MouseEvent> MouseEvent::create(const AtomicString& eventType, PassRefPtr<AbstractView> view, const PlatformMouseEvent& event, int detail, PassRefPtr<Node> relatedTarget) -{ - ASSERT(event.type() == PlatformEvent::MouseMoved || event.button() != NoButton); - bool isMouseEnterOrLeave = eventType == eventNames().mouseenterEvent || eventType == eventNames().mouseleaveEvent; bool isCancelable = eventType != eventNames().mousemoveEvent && !isMouseEnterOrLeave; bool canBubble = !isMouseEnterOrLeave; @@ -66,92 +50,79 @@ PassRefPtr<MouseEvent> MouseEvent::create(const AtomicString& eventType, PassRef event.movementDelta().x(), event.movementDelta().y(), #endif event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey(), event.button(), - relatedTarget); + relatedTarget, event.force(), event.syntheticClickType()); } -PassRefPtr<MouseEvent> MouseEvent::create(const AtomicString& type, bool canBubble, bool cancelable, double timestamp, PassRefPtr<AbstractView> view, - int detail, int screenX, int screenY, int pageX, int pageY, +Ref<MouseEvent> MouseEvent::create(const AtomicString& type, bool canBubble, bool cancelable, double timestamp, DOMWindow* view, int detail, int screenX, int screenY, int pageX, int pageY, #if ENABLE(POINTER_LOCK) int movementX, int movementY, #endif - bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, unsigned short button, - PassRefPtr<EventTarget> relatedTarget) - + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, unsigned short button, EventTarget* relatedTarget, double force, unsigned short syntheticClickType, DataTransfer* dataTransfer, bool isSimulated) { - return MouseEvent::create(type, canBubble, cancelable, timestamp, view, - detail, screenX, screenY, pageX, pageY, + return adoptRef(*new MouseEvent(type, canBubble, cancelable, timestamp, view, + detail, { screenX, screenY }, { pageX, pageY }, #if ENABLE(POINTER_LOCK) - movementX, movementY, + { movementX, movementY }, #endif - ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget, 0, false); + ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget, force, syntheticClickType, dataTransfer, isSimulated)); } -PassRefPtr<MouseEvent> MouseEvent::create(const AtomicString& type, bool canBubble, bool cancelable, double timestamp, PassRefPtr<AbstractView> view, - int detail, int screenX, int screenY, int pageX, int pageY, -#if ENABLE(POINTER_LOCK) - int movementX, int movementY, -#endif - bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, unsigned short button, - PassRefPtr<EventTarget> relatedTarget, PassRefPtr<Clipboard> clipboard, bool isSimulated) +Ref<MouseEvent> MouseEvent::create(const AtomicString& eventType, bool canBubble, bool cancelable, DOMWindow* view, int detail, int screenX, int screenY, int clientX, int clientY, bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, unsigned short button, unsigned short syntheticClickType, EventTarget* relatedTarget) { - return adoptRef(new MouseEvent(type, canBubble, cancelable, timestamp, view, - detail, screenX, screenY, pageX, pageY, -#if ENABLE(POINTER_LOCK) - movementX, movementY, -#endif - ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget, clipboard, isSimulated)); + return adoptRef(*new MouseEvent(eventType, canBubble, cancelable, view, detail, { screenX, screenY }, { clientX, clientY }, ctrlKey, altKey, shiftKey, metaKey, button, syntheticClickType, relatedTarget)); } MouseEvent::MouseEvent() - : m_button(0) - , m_buttonDown(false) { } -MouseEvent::MouseEvent(const AtomicString& eventType, bool canBubble, bool cancelable, double timestamp, PassRefPtr<AbstractView> view, - int detail, int screenX, int screenY, int pageX, int pageY, +MouseEvent::MouseEvent(const AtomicString& eventType, bool canBubble, bool cancelable, double timestamp, DOMWindow* view, int detail, const IntPoint& screenLocation, const IntPoint& windowLocation, #if ENABLE(POINTER_LOCK) - int movementX, int movementY, + const IntPoint& movementDelta, #endif - bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, - unsigned short button, PassRefPtr<EventTarget> relatedTarget, - PassRefPtr<Clipboard> clipboard, bool isSimulated) - : MouseRelatedEvent(eventType, canBubble, cancelable, timestamp, view, detail, IntPoint(screenX, screenY), - IntPoint(pageX, pageY), + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, unsigned short button, EventTarget* relatedTarget, double force, unsigned short syntheticClickType, DataTransfer* dataTransfer, bool isSimulated) + : MouseRelatedEvent(eventType, canBubble, cancelable, timestamp, view, detail, screenLocation, windowLocation, #if ENABLE(POINTER_LOCK) - IntPoint(movementX, movementY), + movementDelta, #endif - ctrlKey, altKey, shiftKey, metaKey, isSimulated) + ctrlKey, altKey, shiftKey, metaKey, isSimulated) , m_button(button == (unsigned short)-1 ? 0 : button) + , m_syntheticClickType(button == (unsigned short)-1 ? 0 : syntheticClickType) , m_buttonDown(button != (unsigned short)-1) , m_relatedTarget(relatedTarget) - , m_clipboard(clipboard) + , m_force(force) + , m_dataTransfer(dataTransfer) { } -MouseEvent::MouseEvent(const AtomicString& eventType, const MouseEventInit& initializer) - : MouseRelatedEvent(eventType, initializer.bubbles, initializer.cancelable, currentTime(), initializer.view, initializer.detail, IntPoint(initializer.screenX, initializer.screenY), - IntPoint(0 /* pageX */, 0 /* pageY */), +MouseEvent::MouseEvent(const AtomicString& eventType, bool canBubble, bool cancelable, DOMWindow* view, int detail, const IntPoint& screenLocation, const IntPoint& clientLocation, bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, unsigned short button, unsigned short syntheticClickType, EventTarget* relatedTarget) + : MouseRelatedEvent(eventType, canBubble, cancelable, WTF::currentTime(), view, detail, screenLocation, { }, #if ENABLE(POINTER_LOCK) - IntPoint(0 /* movementX */, 0 /* movementY */), + { }, #endif - initializer.ctrlKey, initializer.altKey, initializer.shiftKey, initializer.metaKey, false /* isSimulated */) + ctrlKey, altKey, shiftKey, metaKey, false) + , m_button(button == (unsigned short)-1 ? 0 : button) + , m_syntheticClickType(button == (unsigned short)-1 ? 0 : syntheticClickType) + , m_buttonDown(button != (unsigned short)-1) + , m_relatedTarget(relatedTarget) +{ + initCoordinates(clientLocation); +} + +MouseEvent::MouseEvent(const AtomicString& eventType, const MouseEventInit& initializer, IsTrusted isTrusted) + : MouseRelatedEvent(eventType, initializer, isTrusted) , m_button(initializer.button == (unsigned short)-1 ? 0 : initializer.button) , m_buttonDown(initializer.button != (unsigned short)-1) , m_relatedTarget(initializer.relatedTarget) - , m_clipboard(0 /* clipboard */) { - initCoordinates(IntPoint(initializer.clientX, initializer.clientY)); + initCoordinates({ initializer.clientX, initializer.clientY }); } MouseEvent::~MouseEvent() { } -void MouseEvent::initMouseEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<AbstractView> view, - int detail, int screenX, int screenY, int clientX, int clientY, - bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, - unsigned short button, PassRefPtr<EventTarget> relatedTarget) +void MouseEvent::initMouseEvent(const AtomicString& type, bool canBubble, bool cancelable, DOMWindow* view, int detail, int screenX, int screenY, int clientX, int clientY, bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, unsigned short button, EventTarget* relatedTarget) { if (dispatched()) return; @@ -164,13 +135,14 @@ void MouseEvent::initMouseEvent(const AtomicString& type, bool canBubble, bool c m_shiftKey = shiftKey; m_metaKey = metaKey; m_button = button == (unsigned short)-1 ? 0 : button; + m_syntheticClickType = 0; m_buttonDown = button != (unsigned short)-1; m_relatedTarget = relatedTarget; initCoordinates(IntPoint(clientX, clientY)); // FIXME: m_isSimulated is not set to false here. - // FIXME: m_clipboard is not set to 0 here. + // FIXME: m_dataTransfer is not set to 0 here. } EventInterface MouseEvent::eventInterface() const @@ -185,15 +157,28 @@ bool MouseEvent::isMouseEvent() const bool MouseEvent::isDragEvent() const { - const AtomicString& t = type(); - return t == eventNames().dragenterEvent || t == eventNames().dragoverEvent || t == eventNames().dragleaveEvent || t == eventNames().dropEvent - || t == eventNames().dragstartEvent|| t == eventNames().dragEvent || t == eventNames().dragendEvent; + // This function is only used to decide to return nullptr for dataTransfer even when m_dataTransfer is non-null. + // FIXME: Is that really valuable? Why will m_dataTransfer be non-null but we need to return null for dataTransfer? + // Quite peculiar to decide based on the type string; may have been be provided by call to JavaScript constructor. + auto& type = this->type(); + return type == eventNames().dragEvent + || type == eventNames().dragendEvent + || type == eventNames().dragenterEvent + || type == eventNames().dragleaveEvent + || type == eventNames().dragoverEvent + || type == eventNames().dragstartEvent + || type == eventNames().dropEvent; +} + +bool MouseEvent::canTriggerActivationBehavior(const Event& event) +{ + return event.type() == eventNames().clickEvent && (!is<MouseEvent>(event) || downcast<MouseEvent>(event).button() != RightButton); } int MouseEvent::which() const { // For the DOM, the return values for left, middle and right mouse buttons are 0, 1, 2, respectively. - // For the Netscape "which" property, the return values for left, middle and right mouse buttons are 1, 2, 3, respectively. + // For the Netscape "which" property, the return values for left, middle and right mouse buttons are 1, 2, 3, respectively. // So we must add 1. if (!m_buttonDown) return 0; @@ -222,68 +207,4 @@ Node* MouseEvent::fromElement() const return target() ? target()->toNode() : nullptr; } -// FIXME: Fix positioning. e.g. We need to consider border/padding. -// https://bugs.webkit.org/show_bug.cgi?id=93696 -inline static int adjustedClientX(int innerClientX, HTMLIFrameElement* iframe, FrameView* frameView) -{ - return iframe->offsetLeft() - frameView->scrollX() + innerClientX; -} - -inline static int adjustedClientY(int innerClientY, HTMLIFrameElement* iframe, FrameView* frameView) -{ - return iframe->offsetTop() - frameView->scrollY() + innerClientY; -} - -PassRefPtr<Event> MouseEvent::cloneFor(HTMLIFrameElement* iframe) const -{ - ASSERT(iframe); - RefPtr<MouseEvent> clonedMouseEvent = MouseEvent::create(); - Frame* frame = iframe->document().frame(); - FrameView* frameView = frame ? frame->view() : 0; - clonedMouseEvent->initMouseEvent(type(), bubbles(), cancelable(), - iframe->document().defaultView(), - detail(), screenX(), screenY(), - frameView ? adjustedClientX(clientX(), iframe, frameView) : 0, - frameView ? adjustedClientY(clientY(), iframe, frameView) : 0, - ctrlKey(), altKey(), shiftKey(), metaKey(), - button(), - // Nullifies relatedTarget. - 0); - return clonedMouseEvent.release(); -} - -PassRefPtr<SimulatedMouseEvent> SimulatedMouseEvent::create(const AtomicString& eventType, PassRefPtr<AbstractView> view, PassRefPtr<Event> underlyingEvent, Element* target) -{ - return adoptRef(new SimulatedMouseEvent(eventType, view, underlyingEvent, target)); -} - -SimulatedMouseEvent::~SimulatedMouseEvent() -{ -} - -SimulatedMouseEvent::SimulatedMouseEvent(const AtomicString& eventType, PassRefPtr<AbstractView> view, PassRefPtr<Event> underlyingEvent, Element* target) - : MouseEvent(eventType, true, true, underlyingEvent ? underlyingEvent->timeStamp() : currentTime(), view, 0, 0, 0, 0, 0, -#if ENABLE(POINTER_LOCK) - 0, 0, -#endif - false, false, false, false, 0, 0, 0, true) -{ - if (UIEventWithKeyState* keyStateEvent = findEventWithKeyState(underlyingEvent.get())) { - m_ctrlKey = keyStateEvent->ctrlKey(); - m_altKey = keyStateEvent->altKey(); - m_shiftKey = keyStateEvent->shiftKey(); - m_metaKey = keyStateEvent->metaKey(); - } - setUnderlyingEvent(underlyingEvent); - - if (this->underlyingEvent() && this->underlyingEvent()->isMouseEvent()) { - MouseEvent* mouseEvent = static_cast<MouseEvent*>(this->underlyingEvent()); - m_screenLocation = mouseEvent->screenLocation(); - initCoordinates(mouseEvent->clientLocation()); - } else if (target) { - m_screenLocation = target->screenRect().center(); - initCoordinates(LayoutPoint(target->clientRect().center())); - } -} - } // namespace WebCore diff --git a/Source/WebCore/dom/MouseEvent.h b/Source/WebCore/dom/MouseEvent.h index 3b550cb8c..3730ca430 100644 --- a/Source/WebCore/dom/MouseEvent.h +++ b/Source/WebCore/dom/MouseEvent.h @@ -2,7 +2,7 @@ * Copyright (C) 2001 Peter Kelly (pmk@post.com) * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2003-2016 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -21,128 +21,87 @@ * */ -#ifndef MouseEvent_h -#define MouseEvent_h +#pragma once +#include "MouseEventInit.h" #include "MouseRelatedEvent.h" namespace WebCore { -class Clipboard; +class DataTransfer; class PlatformMouseEvent; -struct MouseEventInit : public UIEventInit { - MouseEventInit(); - - int screenX; - int screenY; - int clientX; - int clientY; - bool ctrlKey; - bool altKey; - bool shiftKey; - bool metaKey; - unsigned short button; - RefPtr<EventTarget> relatedTarget; -}; - class MouseEvent : public MouseRelatedEvent { public: - static PassRefPtr<MouseEvent> create() - { - return adoptRef(new MouseEvent); - } - - static PassRefPtr<MouseEvent> create(const AtomicString& type, bool canBubble, bool cancelable, double timestamp, PassRefPtr<AbstractView>, - int detail, int screenX, int screenY, int pageX, int pageY, + WEBCORE_EXPORT static Ref<MouseEvent> create(const AtomicString& type, bool canBubble, bool cancelable, double timestamp, DOMWindow*, int detail, int screenX, int screenY, int pageX, int pageY, #if ENABLE(POINTER_LOCK) int movementX, int movementY, #endif - bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, unsigned short button, - PassRefPtr<EventTarget> relatedTarget); + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, unsigned short button, EventTarget* relatedTarget, double force, unsigned short syntheticClickType, DataTransfer* = nullptr, bool isSimulated = false); - static PassRefPtr<MouseEvent> create(const AtomicString& type, bool canBubble, bool cancelable, double timestamp, PassRefPtr<AbstractView>, - int detail, int screenX, int screenY, int pageX, int pageY, -#if ENABLE(POINTER_LOCK) - int movementX, int movementY, -#endif - bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, unsigned short button, - PassRefPtr<EventTarget> relatedTarget, PassRefPtr<Clipboard>, bool isSimulated = false); + WEBCORE_EXPORT static Ref<MouseEvent> create(const AtomicString& eventType, DOMWindow*, const PlatformMouseEvent&, int detail, Node* relatedTarget); + + static Ref<MouseEvent> create(const AtomicString& eventType, bool canBubble, bool cancelable, DOMWindow*, int detail, int screenX, int screenY, int clientX, int clientY, bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, unsigned short button, unsigned short syntheticClickType, EventTarget* relatedTarget); - static PassRefPtr<MouseEvent> create(const AtomicString& eventType, PassRefPtr<AbstractView>, const PlatformMouseEvent&, int detail, PassRefPtr<Node> relatedTarget); + static Ref<MouseEvent> createForBindings() { return adoptRef(*new MouseEvent); } - static PassRefPtr<MouseEvent> create(const AtomicString& eventType, const MouseEventInit&); + static Ref<MouseEvent> create(const AtomicString& eventType, const MouseEventInit&, IsTrusted = IsTrusted::No); virtual ~MouseEvent(); - void initMouseEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<AbstractView>, - int detail, int screenX, int screenY, int clientX, int clientY, - bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, - unsigned short button, PassRefPtr<EventTarget> relatedTarget); + WEBCORE_EXPORT void initMouseEvent(const AtomicString& type, bool canBubble, bool cancelable, DOMWindow*, int detail, int screenX, int screenY, int clientX, int clientY, bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, unsigned short button, EventTarget* relatedTarget); // WinIE uses 1,4,2 for left/middle/right but not for click (just for mousedown/up, maybe others), // but we will match the standard DOM. unsigned short button() const { return m_button; } + unsigned short syntheticClickType() const { return m_syntheticClickType; } bool buttonDown() const { return m_buttonDown; } - virtual EventTarget* relatedTarget() const override final { return m_relatedTarget.get(); } - void setRelatedTarget(PassRefPtr<EventTarget> relatedTarget) { m_relatedTarget = relatedTarget; } - - Clipboard* clipboard() const override { return m_clipboard.get(); } + EventTarget* relatedTarget() const final { return m_relatedTarget.get(); } + void setRelatedTarget(EventTarget* relatedTarget) { m_relatedTarget = relatedTarget; } + double force() const { return m_force; } + void setForce(double force) { m_force = force; } - Node* toElement() const; - Node* fromElement() const; + WEBCORE_EXPORT Node* toElement() const; + WEBCORE_EXPORT Node* fromElement() const; - Clipboard* dataTransfer() const { return isDragEvent() ? m_clipboard.get() : 0; } + DataTransfer* dataTransfer() const { return isDragEvent() ? m_dataTransfer.get() : nullptr; } - virtual EventInterface eventInterface() const override; + static bool canTriggerActivationBehavior(const Event&); - virtual bool isMouseEvent() const override; - virtual bool isDragEvent() const override; - virtual int which() const override; - - virtual PassRefPtr<Event> cloneFor(HTMLIFrameElement*) const override; + int which() const final; protected: - MouseEvent(const AtomicString& type, bool canBubble, bool cancelable, double timestamp, PassRefPtr<AbstractView>, - int detail, int screenX, int screenY, int pageX, int pageY, + MouseEvent(const AtomicString& type, bool canBubble, bool cancelable, double timestamp, DOMWindow*, + int detail, const IntPoint& screenLocation, const IntPoint& windowLocation, #if ENABLE(POINTER_LOCK) - int movementX, int movementY, + const IntPoint& movementDelta, #endif bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, unsigned short button, - PassRefPtr<EventTarget> relatedTarget, PassRefPtr<Clipboard>, bool isSimulated); + EventTarget* relatedTarget, double force, unsigned short syntheticClickType, DataTransfer*, bool isSimulated); + + MouseEvent(const AtomicString& type, bool canBubble, bool cancelable, DOMWindow*, + int detail, const IntPoint& screenLocation, const IntPoint& clientLocation, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, + unsigned short button, unsigned short syntheticClickType, EventTarget* relatedTarget); - MouseEvent(const AtomicString& type, const MouseEventInit&); + MouseEvent(const AtomicString& type, const MouseEventInit&, IsTrusted); MouseEvent(); private: - unsigned short m_button; - bool m_buttonDown; - RefPtr<EventTarget> m_relatedTarget; - RefPtr<Clipboard> m_clipboard; -}; + bool isMouseEvent() const final; + EventInterface eventInterface() const override; -class SimulatedMouseEvent : public MouseEvent { -public: - static PassRefPtr<SimulatedMouseEvent> create(const AtomicString& eventType, PassRefPtr<AbstractView>, PassRefPtr<Event> underlyingEvent, Element* target); - virtual ~SimulatedMouseEvent(); + bool isDragEvent() const; -private: - SimulatedMouseEvent(const AtomicString& eventType, PassRefPtr<AbstractView>, PassRefPtr<Event> underlyingEvent, Element* target); + unsigned short m_button { 0 }; + unsigned short m_syntheticClickType { 0 }; + bool m_buttonDown { false }; + RefPtr<EventTarget> m_relatedTarget; + double m_force { 0 }; + RefPtr<DataTransfer> m_dataTransfer; }; -inline MouseEvent* toMouseEvent(Event* event) -{ - ASSERT_WITH_SECURITY_IMPLICATION(event && event->isMouseEvent()); - return static_cast<MouseEvent*>(event); -} - -inline MouseEvent& toMouseEvent(Event& event) -{ - ASSERT(event.isMouseEvent()); - return static_cast<MouseEvent&>(event); -} - } // namespace WebCore -#endif // MouseEvent_h +SPECIALIZE_TYPE_TRAITS_EVENT(MouseEvent) diff --git a/Source/WebCore/dom/MouseEvent.idl b/Source/WebCore/dom/MouseEvent.idl index 7d3524051..7d6f60d60 100644 --- a/Source/WebCore/dom/MouseEvent.idl +++ b/Source/WebCore/dom/MouseEvent.idl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2015 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -18,48 +18,38 @@ */ [ - ConstructorConditional=DOM4_EVENTS_CONSTRUCTOR, - ConstructorTemplate=Event, + Constructor(DOMString type, optional MouseEventInit eventInitDict), + DoNotCheckConstants, ] interface MouseEvent : UIEvent { - [InitializedByEventConstructor] readonly attribute long screenX; - [InitializedByEventConstructor] readonly attribute long screenY; - [InitializedByEventConstructor] readonly attribute long clientX; - [InitializedByEventConstructor] readonly attribute long clientY; - [InitializedByEventConstructor] readonly attribute boolean ctrlKey; - [InitializedByEventConstructor] readonly attribute boolean shiftKey; - [InitializedByEventConstructor] readonly attribute boolean altKey; - [InitializedByEventConstructor] readonly attribute boolean metaKey; - [InitializedByEventConstructor] readonly attribute unsigned short button; - [InitializedByEventConstructor] readonly attribute EventTarget relatedTarget; - [Conditional=POINTER_LOCK] readonly attribute long webkitMovementX; - [Conditional=POINTER_LOCK] readonly attribute long webkitMovementY; - - [ObjCLegacyUnnamedParameters] void initMouseEvent([Default=Undefined] optional DOMString type, - [Default=Undefined] optional boolean canBubble, - [Default=Undefined] optional boolean cancelable, - [Default=Undefined] optional DOMWindow view, - [Default=Undefined] optional long detail, - [Default=Undefined] optional long screenX, - [Default=Undefined] optional long screenY, - [Default=Undefined] optional long clientX, - [Default=Undefined] optional long clientY, - [Default=Undefined] optional boolean ctrlKey, - [Default=Undefined] optional boolean altKey, - [Default=Undefined] optional boolean shiftKey, - [Default=Undefined] optional boolean metaKey, - [Default=Undefined] optional unsigned short button, - [Default=Undefined] optional EventTarget relatedTarget); + [Conditional=MOUSE_FORCE_EVENTS] const double WEBKIT_FORCE_AT_MOUSE_DOWN = 1; + [Conditional=MOUSE_FORCE_EVENTS] const double WEBKIT_FORCE_AT_FORCE_MOUSE_DOWN = 2; - // extensions - readonly attribute long offsetX; - readonly attribute long offsetY; - readonly attribute long x; - readonly attribute long y; - readonly attribute Node fromElement; - readonly attribute Node toElement; + readonly attribute long screenX; + readonly attribute long screenY; + readonly attribute long clientX; + readonly attribute long clientY; + readonly attribute boolean ctrlKey; + readonly attribute boolean shiftKey; + readonly attribute boolean altKey; + readonly attribute boolean metaKey; + readonly attribute unsigned short button; + readonly attribute EventTarget? relatedTarget; + [Conditional=POINTER_LOCK] readonly attribute long movementX; + [Conditional=POINTER_LOCK] readonly attribute long movementY; + [Conditional=MOUSE_FORCE_EVENTS, ImplementedAs=force]readonly attribute double webkitForce; -#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT - readonly attribute Clipboard dataTransfer; -#endif -}; + // FIXME: Using "undefined" as default parameter value is wrong. + void initMouseEvent(optional DOMString type = "undefined", optional boolean canBubble = false, optional boolean cancelable = false, + optional DOMWindow? view = null, optional long detail = 0, + optional long screenX = 0, optional long screenY = 0, optional long clientX = 0, optional long clientY = 0, + optional boolean ctrlKey = false, optional boolean altKey = false, optional boolean shiftKey = false, optional boolean metaKey = false, + optional unsigned short button = 0, optional EventTarget? relatedTarget = null); + readonly attribute long offsetX; + readonly attribute long offsetY; + readonly attribute long x; + readonly attribute long y; + readonly attribute Node? fromElement; + readonly attribute Node? toElement; + readonly attribute DataTransfer dataTransfer; +}; diff --git a/Source/WebCore/dom/MouseEventInit.h b/Source/WebCore/dom/MouseEventInit.h new file mode 100644 index 000000000..7391e29c3 --- /dev/null +++ b/Source/WebCore/dom/MouseEventInit.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "MouseRelatedEvent.h" + +namespace WebCore { + +struct MouseEventInit : MouseRelatedEventInit { + int clientX { 0 }; + int clientY { 0 }; + unsigned short button { 0 }; + RefPtr<EventTarget> relatedTarget; +}; + +} diff --git a/Source/WebCore/dom/MouseEventInit.idl b/Source/WebCore/dom/MouseEventInit.idl new file mode 100644 index 000000000..aa1abd11f --- /dev/null +++ b/Source/WebCore/dom/MouseEventInit.idl @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +dictionary MouseEventInit : EventModifierInit { + long screenX = 0; + long screenY = 0; + long clientX = 0; + long clientY = 0; + unsigned short button = 0; + + // FIXME: We need to support the following member. + // unsigned short buttons = 0; + + EventTarget? relatedTarget = null; +}; diff --git a/Source/WebCore/dom/MouseRelatedEvent.cpp b/Source/WebCore/dom/MouseRelatedEvent.cpp index 88000f5f3..829126b2c 100644 --- a/Source/WebCore/dom/MouseRelatedEvent.cpp +++ b/Source/WebCore/dom/MouseRelatedEvent.cpp @@ -37,11 +37,11 @@ MouseRelatedEvent::MouseRelatedEvent() { } -static LayoutSize contentsScrollOffset(AbstractView* abstractView) +static LayoutSize contentsScrollOffset(DOMWindow* DOMWindow) { - if (!abstractView) + if (!DOMWindow) return LayoutSize(); - Frame* frame = abstractView->frame(); + Frame* frame = DOMWindow->frame(); if (!frame) return LayoutSize(); FrameView* frameView = frame->view(); @@ -55,35 +55,47 @@ static LayoutSize contentsScrollOffset(AbstractView* abstractView) #endif } -MouseRelatedEvent::MouseRelatedEvent(const AtomicString& eventType, bool canBubble, bool cancelable, double timestamp, PassRefPtr<AbstractView> abstractView, +MouseRelatedEvent::MouseRelatedEvent(const AtomicString& eventType, bool canBubble, bool cancelable, double timestamp, DOMWindow* DOMWindow, int detail, const IntPoint& screenLocation, const IntPoint& windowLocation, #if ENABLE(POINTER_LOCK) const IntPoint& movementDelta, #endif bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, bool isSimulated) - : UIEventWithKeyState(eventType, canBubble, cancelable, timestamp, abstractView, detail, ctrlKey, altKey, shiftKey, metaKey) + : UIEventWithKeyState(eventType, canBubble, cancelable, timestamp, DOMWindow, detail, ctrlKey, altKey, shiftKey, metaKey, false, false) , m_screenLocation(screenLocation) #if ENABLE(POINTER_LOCK) , m_movementDelta(movementDelta) #endif , m_isSimulated(isSimulated) { + init(isSimulated, windowLocation); +} + +MouseRelatedEvent::MouseRelatedEvent(const AtomicString& eventType, const MouseRelatedEventInit& initializer, IsTrusted isTrusted) + : UIEventWithKeyState(eventType, initializer, isTrusted) + , m_screenLocation(IntPoint(initializer.screenX, initializer.screenY)) +#if ENABLE(POINTER_LOCK) + , m_movementDelta(IntPoint(0, 0)) +#endif + , m_isSimulated(false) +{ + init(false, IntPoint(0, 0)); +} + +void MouseRelatedEvent::init(bool isSimulated, const IntPoint& windowLocation) +{ LayoutPoint adjustedPageLocation; LayoutPoint scrollPosition; - Frame* frame = view() ? view()->frame() : 0; + Frame* frame = view() ? view()->frame() : nullptr; if (frame && !isSimulated) { if (FrameView* frameView = frame->view()) { -#if !PLATFORM(IOS) - scrollPosition = frameView->scrollPosition(); -#else - scrollPosition = frameView->actualScrollPosition(); -#endif + scrollPosition = frameView->contentsScrollPosition(); adjustedPageLocation = frameView->windowToContents(windowLocation); float scaleFactor = 1 / (frame->pageZoomFactor() * frame->frameScaleFactor()); if (scaleFactor != 1.0f) { - adjustedPageLocation.scale(scaleFactor, scaleFactor); - scrollPosition.scale(scaleFactor, scaleFactor); + adjustedPageLocation.scale(scaleFactor); + scrollPosition.scale(scaleFactor); } } } @@ -144,7 +156,7 @@ static float frameScaleFactor(const UIEvent* event) void MouseRelatedEvent::computePageLocation() { float scaleFactor = pageZoomFactor(this) * frameScaleFactor(this); - setAbsoluteLocation(roundedLayoutPoint(FloatPoint(pageX() * scaleFactor, pageY() * scaleFactor))); + setAbsoluteLocation(LayoutPoint(pageX() * scaleFactor, pageY() * scaleFactor)); } void MouseRelatedEvent::receivedTarget() @@ -154,7 +166,7 @@ void MouseRelatedEvent::receivedTarget() void MouseRelatedEvent::computeRelativePosition() { - Node* targetNode = target() ? target()->toNode() : 0; + Node* targetNode = target() ? target()->toNode() : nullptr; if (!targetNode) return; @@ -167,11 +179,10 @@ void MouseRelatedEvent::computeRelativePosition() // Adjust offsetLocation to be relative to the target's position. if (RenderObject* r = targetNode->renderer()) { - FloatPoint localPos = r->absoluteToLocal(absoluteLocation(), UseTransforms); - m_offsetLocation = roundedLayoutPoint(localPos); + m_offsetLocation = LayoutPoint(r->absoluteToLocal(absoluteLocation(), UseTransforms)); float scaleFactor = 1 / (pageZoomFactor(this) * frameScaleFactor(this)); if (scaleFactor != 1.0f) - m_offsetLocation.scale(scaleFactor, scaleFactor); + m_offsetLocation.scale(scaleFactor); } // Adjust layerLocation to be relative to the layer. @@ -208,6 +219,8 @@ int MouseRelatedEvent::layerY() int MouseRelatedEvent::offsetX() { + if (isSimulated()) + return 0; if (!m_hasCachedRelativePosition) computeRelativePosition(); return roundToInt(m_offsetLocation.x()); @@ -215,6 +228,8 @@ int MouseRelatedEvent::offsetX() int MouseRelatedEvent::offsetY() { + if (isSimulated()) + return 0; if (!m_hasCachedRelativePosition) computeRelativePosition(); return roundToInt(m_offsetLocation.y()); diff --git a/Source/WebCore/dom/MouseRelatedEvent.h b/Source/WebCore/dom/MouseRelatedEvent.h index 8079061e4..5f8dc0320 100644 --- a/Source/WebCore/dom/MouseRelatedEvent.h +++ b/Source/WebCore/dom/MouseRelatedEvent.h @@ -2,7 +2,7 @@ * Copyright (C) 2001 Peter Kelly (pmk@post.com) * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - * Copyright (C) 2003, 2004, 2005, 2006, 2013 Apple Computer, Inc. + * Copyright (C) 2003, 2004, 2005, 2006, 2013 Apple Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -21,77 +21,82 @@ * */ -#ifndef MouseRelatedEvent_h -#define MouseRelatedEvent_h +#pragma once #include "LayoutPoint.h" #include "UIEventWithKeyState.h" namespace WebCore { - // Internal only: Helper class for what's common between mouse and wheel events. - class MouseRelatedEvent : public UIEventWithKeyState { - public: - // Note that these values are adjusted to counter the effects of zoom, so that values - // exposed via DOM APIs are invariant under zooming. - int screenX() const { return m_screenLocation.x(); } - int screenY() const { return m_screenLocation.y(); } - const IntPoint& screenLocation() const { return m_screenLocation; } - int clientX() const { return m_clientLocation.x(); } - int clientY() const { return m_clientLocation.y(); } +struct MouseRelatedEventInit : public EventModifierInit { + int screenX { 0 }; + int screenY { 0 }; +}; + +// Internal only: Helper class for what's common between mouse and wheel events. +class MouseRelatedEvent : public UIEventWithKeyState { +public: + // Note that these values are adjusted to counter the effects of zoom, so that values + // exposed via DOM APIs are invariant under zooming. + int screenX() const { return m_screenLocation.x(); } + int screenY() const { return m_screenLocation.y(); } + const IntPoint& screenLocation() const { return m_screenLocation; } + int clientX() const { return m_clientLocation.x(); } + int clientY() const { return m_clientLocation.y(); } #if ENABLE(POINTER_LOCK) - int webkitMovementX() const { return m_movementDelta.x(); } - int webkitMovementY() const { return m_movementDelta.y(); } + int movementX() const { return m_movementDelta.x(); } + int movementY() const { return m_movementDelta.y(); } #endif - const LayoutPoint& clientLocation() const { return m_clientLocation; } - int layerX() override; - int layerY() override; - int offsetX(); - int offsetY(); - bool isSimulated() const { return m_isSimulated; } - virtual int pageX() const override; - virtual int pageY() const override; - virtual const LayoutPoint& pageLocation() const; - int x() const; - int y() const; + const LayoutPoint& clientLocation() const { return m_clientLocation; } + int layerX() override; + int layerY() override; + WEBCORE_EXPORT int offsetX(); + WEBCORE_EXPORT int offsetY(); + bool isSimulated() const { return m_isSimulated; } + int pageX() const final; + int pageY() const final; + virtual const LayoutPoint& pageLocation() const; + WEBCORE_EXPORT int x() const; + WEBCORE_EXPORT int y() const; - // Page point in "absolute" coordinates (i.e. post-zoomed, page-relative coords, - // usable with RenderObject::absoluteToLocal). - const LayoutPoint& absoluteLocation() const { return m_absoluteLocation; } - void setAbsoluteLocation(const LayoutPoint& p) { m_absoluteLocation = p; } + // Page point in "absolute" coordinates (i.e. post-zoomed, page-relative coords, + // usable with RenderObject::absoluteToLocal). + const LayoutPoint& absoluteLocation() const { return m_absoluteLocation; } + void setAbsoluteLocation(const LayoutPoint& p) { m_absoluteLocation = p; } - protected: - MouseRelatedEvent(); - MouseRelatedEvent(const AtomicString& type, bool canBubble, bool cancelable, double timestamp, PassRefPtr<AbstractView>, - int detail, const IntPoint& screenLocation, const IntPoint& windowLocation, +protected: + MouseRelatedEvent(); + MouseRelatedEvent(const AtomicString& type, bool canBubble, bool cancelable, double timestamp, DOMWindow*, + int detail, const IntPoint& screenLocation, const IntPoint& windowLocation, #if ENABLE(POINTER_LOCK) - const IntPoint& movementDelta, + const IntPoint& movementDelta, #endif - bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, bool isSimulated = false); + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, bool isSimulated = false); + MouseRelatedEvent(const AtomicString& type, const MouseRelatedEventInit&, IsTrusted = IsTrusted::No); - void initCoordinates(); - void initCoordinates(const LayoutPoint& clientLocation); - virtual void receivedTarget() override; + void initCoordinates(); + void initCoordinates(const LayoutPoint& clientLocation); + void receivedTarget() final; - void computePageLocation(); - void computeRelativePosition(); + void computePageLocation(); + void computeRelativePosition(); - // Expose these so MouseEvent::initMouseEvent can set them. - IntPoint m_screenLocation; - LayoutPoint m_clientLocation; + // Expose these so MouseEvent::initMouseEvent can set them. + IntPoint m_screenLocation; + LayoutPoint m_clientLocation; #if ENABLE(POINTER_LOCK) - LayoutPoint m_movementDelta; + LayoutPoint m_movementDelta; #endif - private: - LayoutPoint m_pageLocation; - LayoutPoint m_layerLocation; - LayoutPoint m_offsetLocation; - LayoutPoint m_absoluteLocation; - bool m_isSimulated; - bool m_hasCachedRelativePosition; - }; +private: + void init(bool isSimulated, const IntPoint&); -} // namespace WebCore + LayoutPoint m_pageLocation; + LayoutPoint m_layerLocation; + LayoutPoint m_offsetLocation; + LayoutPoint m_absoluteLocation; + bool m_isSimulated; + bool m_hasCachedRelativePosition; +}; -#endif // MouseRelatedEvent_h +} // namespace WebCore diff --git a/Source/WebCore/dom/MutationCallback.h b/Source/WebCore/dom/MutationCallback.h index e698a59c3..4658c1929 100644 --- a/Source/WebCore/dom/MutationCallback.h +++ b/Source/WebCore/dom/MutationCallback.h @@ -28,8 +28,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef MutationCallback_h -#define MutationCallback_h +#pragma once #include <wtf/RefCounted.h> #include <wtf/RefPtr.h> @@ -44,10 +43,8 @@ class MutationCallback : public RefCounted<MutationCallback> { public: virtual ~MutationCallback() { } - virtual void call(const Vector<RefPtr<MutationRecord>>&, MutationObserver*) = 0; - virtual ScriptExecutionContext* scriptExecutionContext() const = 0; + virtual void call(const Vector<Ref<MutationRecord>>&, MutationObserver*) = 0; + virtual bool canInvokeCallback() const = 0; }; -} - -#endif // MutationCallback_h +} // namespace WebCore diff --git a/Source/WebCore/dom/MutationEvent.cpp b/Source/WebCore/dom/MutationEvent.cpp index a61ef767e..6422ad7ba 100644 --- a/Source/WebCore/dom/MutationEvent.cpp +++ b/Source/WebCore/dom/MutationEvent.cpp @@ -23,35 +23,19 @@ #include "config.h" #include "MutationEvent.h" - namespace WebCore { -MutationEvent::MutationEvent() - : m_attrChange(0) -{ -} - -MutationEvent::MutationEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<Node> relatedNode, - const String& prevValue, const String& newValue, - const String& attrName, unsigned short attrChange) +MutationEvent::MutationEvent(const AtomicString& type, bool canBubble, bool cancelable, Node* relatedNode, const String& prevValue, const String& newValue) : Event(type, canBubble, cancelable) , m_relatedNode(relatedNode) , m_prevValue(prevValue) , m_newValue(newValue) - , m_attrName(attrName) - , m_attrChange(attrChange) -{ -} - -MutationEvent::~MutationEvent() { } -void MutationEvent::initMutationEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<Node> relatedNode, - const String& prevValue, const String& newValue, - const String& attrName, unsigned short attrChange) +void MutationEvent::initMutationEvent(const AtomicString& type, bool canBubble, bool cancelable, Node* relatedNode, const String& prevValue, const String& newValue, const String& attrName, unsigned short attrChange) { - if (dispatched()) + if (isBeingDispatched()) return; initEvent(type, canBubble, cancelable); diff --git a/Source/WebCore/dom/MutationEvent.h b/Source/WebCore/dom/MutationEvent.h index cbd360090..f48e855ad 100644 --- a/Source/WebCore/dom/MutationEvent.h +++ b/Source/WebCore/dom/MutationEvent.h @@ -21,60 +21,50 @@ * */ -#ifndef MutationEvent_h -#define MutationEvent_h +#pragma once #include "Event.h" #include "Node.h" namespace WebCore { - class MutationEvent : public Event { - public: - virtual ~MutationEvent(); - - enum attrChangeType { - MODIFICATION = 1, - ADDITION = 2, - REMOVAL = 3 - }; +class MutationEvent final : public Event { +public: + enum { + MODIFICATION = 1, + ADDITION = 2, + REMOVAL = 3 + }; - static PassRefPtr<MutationEvent> create() - { - return adoptRef(new MutationEvent); - } + static Ref<MutationEvent> create(const AtomicString& type, bool canBubble, Node* relatedNode = nullptr, const String& prevValue = String(), const String& newValue = String()) + { + return adoptRef(*new MutationEvent(type, canBubble, false, relatedNode, prevValue, newValue)); + } - static PassRefPtr<MutationEvent> create(const AtomicString& type, bool canBubble, PassRefPtr<Node> relatedNode = 0, - const String& prevValue = String(), const String& newValue = String(), const String& attrName = String(), unsigned short attrChange = 0) - { - return adoptRef(new MutationEvent(type, canBubble, false, relatedNode, prevValue, newValue, attrName, attrChange)); - } + static Ref<MutationEvent> createForBindings() + { + return adoptRef(*new MutationEvent); + } - void initMutationEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<Node> relatedNode, - const String& prevValue, const String& newValue, - const String& attrName, unsigned short attrChange); + WEBCORE_EXPORT void initMutationEvent(const AtomicString& type, bool canBubble, bool cancelable, Node* relatedNode, const String& prevValue, const String& newValue, const String& attrName, unsigned short attrChange); - Node* relatedNode() const { return m_relatedNode.get(); } - String prevValue() const { return m_prevValue; } - String newValue() const { return m_newValue; } - String attrName() const { return m_attrName; } - unsigned short attrChange() const { return m_attrChange; } + Node* relatedNode() const { return m_relatedNode.get(); } + String prevValue() const { return m_prevValue; } + String newValue() const { return m_newValue; } + String attrName() const { return m_attrName; } + unsigned short attrChange() const { return m_attrChange; } - virtual EventInterface eventInterface() const override; +private: + MutationEvent() = default; + MutationEvent(const AtomicString& type, bool canBubble, bool cancelable, Node* relatedNode, const String& prevValue, const String& newValue); - private: - MutationEvent(); - MutationEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<Node> relatedNode, - const String& prevValue, const String& newValue, - const String& attrName, unsigned short attrChange); + EventInterface eventInterface() const final; - RefPtr<Node> m_relatedNode; - String m_prevValue; - String m_newValue; - String m_attrName; - unsigned short m_attrChange; - }; + RefPtr<Node> m_relatedNode; + String m_prevValue; + String m_newValue; + String m_attrName; + unsigned short m_attrChange { 0 }; +}; } // namespace WebCore - -#endif // MutationEvent_h diff --git a/Source/WebCore/dom/MutationEvent.idl b/Source/WebCore/dom/MutationEvent.idl index 5c0e7094c..448c1ab01 100644 --- a/Source/WebCore/dom/MutationEvent.idl +++ b/Source/WebCore/dom/MutationEvent.idl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006 Apple Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -17,28 +17,20 @@ * Boston, MA 02110-1301, USA. */ -// Introduced in DOM Level 2: interface MutationEvent : Event { - // attrChangeType const unsigned short MODIFICATION = 1; - const unsigned short ADDITION = 2; - const unsigned short REMOVAL = 3; + const unsigned short ADDITION = 2; + const unsigned short REMOVAL = 3; - readonly attribute Node relatedNode; - readonly attribute DOMString prevValue; - readonly attribute DOMString newValue; - readonly attribute DOMString attrName; + readonly attribute Node? relatedNode; + readonly attribute DOMString prevValue; + readonly attribute DOMString newValue; + readonly attribute DOMString attrName; readonly attribute unsigned short attrChange; - [ObjCLegacyUnnamedParameters] void initMutationEvent([Default=Undefined] optional DOMString type, - [Default=Undefined] optional boolean canBubble, - [Default=Undefined] optional boolean cancelable, - [Default=Undefined] optional Node relatedNode, - [Default=Undefined] optional DOMString prevValue, - [Default=Undefined] optional DOMString newValue, - [Default=Undefined] optional DOMString attrName, - [Default=Undefined] optional unsigned short attrChange); - + // FIXME: Using "undefined" as default parameter value is wrong. + void initMutationEvent(optional DOMString type = "undefined", optional boolean canBubble = false, optional boolean cancelable = false, + optional Node? relatedNode = null, optional DOMString prevValue = "undefined", optional DOMString newValue = "undefined", + optional DOMString attrName = "undefined", optional unsigned short attrChange = 0); }; - diff --git a/Source/WebCore/dom/MutationObserver.cpp b/Source/WebCore/dom/MutationObserver.cpp index bf00fac40..0b4b1f90d 100644 --- a/Source/WebCore/dom/MutationObserver.cpp +++ b/Source/WebCore/dom/MutationObserver.cpp @@ -32,9 +32,10 @@ #include "MutationObserver.h" -#include "Dictionary.h" #include "Document.h" #include "ExceptionCode.h" +#include "HTMLSlotElement.h" +#include "Microtasks.h" #include "MutationCallback.h" #include "MutationObserverRegistration.h" #include "MutationRecord.h" @@ -45,14 +46,14 @@ namespace WebCore { static unsigned s_observerPriority = 0; -PassRefPtr<MutationObserver> MutationObserver::create(PassRefPtr<MutationCallback> callback) +Ref<MutationObserver> MutationObserver::create(Ref<MutationCallback>&& callback) { ASSERT(isMainThread()); - return adoptRef(new MutationObserver(callback)); + return adoptRef(*new MutationObserver(WTFMove(callback))); } -MutationObserver::MutationObserver(PassRefPtr<MutationCallback> callback) - : m_callback(callback) +MutationObserver::MutationObserver(Ref<MutationCallback>&& callback) + : m_callback(WTFMove(callback)) , m_priority(s_observerPriority++) { } @@ -70,46 +71,43 @@ bool MutationObserver::validateOptions(MutationObserverOptions options) && ((options & CharacterData) || !(options & CharacterDataOldValue)); } -void MutationObserver::observe(Node* node, const Dictionary& optionsDictionary, ExceptionCode& ec) +ExceptionOr<void> MutationObserver::observe(Node& node, const Init& init) { - if (!node) { - ec = NOT_FOUND_ERR; - return; - } - - static const struct { - const char* name; - MutationObserverOptions value; - } booleanOptions[] = { - { "childList", ChildList }, - { "attributes", Attributes }, - { "characterData", CharacterData }, - { "subtree", Subtree }, - { "attributeOldValue", AttributeOldValue }, - { "characterDataOldValue", CharacterDataOldValue } - }; MutationObserverOptions options = 0; - for (unsigned i = 0; i < sizeof(booleanOptions) / sizeof(booleanOptions[0]); ++i) { - bool value = false; - if (optionsDictionary.get(booleanOptions[i].name, value) && value) - options |= booleanOptions[i].value; - } + + if (init.childList) + options |= ChildList; + if (init.subtree) + options |= Subtree; + if (init.attributeOldValue.value_or(false)) + options |= AttributeOldValue; + if (init.characterDataOldValue.value_or(false)) + options |= CharacterDataOldValue; HashSet<AtomicString> attributeFilter; - if (optionsDictionary.get("attributeFilter", attributeFilter)) + if (init.attributeFilter) { + for (auto& value : init.attributeFilter.value()) + attributeFilter.add(value); options |= AttributeFilter; - - if (!validateOptions(options)) { - ec = SYNTAX_ERR; - return; } - node->registerMutationObserver(this, options, attributeFilter); + if (init.attributes ? init.attributes.value() : (options & (AttributeFilter | AttributeOldValue))) + options |= Attributes; + + if (init.characterData ? init.characterData.value() : (options & CharacterDataOldValue)) + options |= CharacterData; + + if (!validateOptions(options)) + return Exception { TypeError }; + + node.registerMutationObserver(*this, options, attributeFilter); + + return { }; } -Vector<RefPtr<MutationRecord>> MutationObserver::takeRecords() +Vector<Ref<MutationRecord>> MutationObserver::takeRecords() { - Vector<RefPtr<MutationRecord>> records; + Vector<Ref<MutationRecord>> records; records.swap(m_records); return records; } @@ -118,60 +116,100 @@ void MutationObserver::disconnect() { m_records.clear(); HashSet<MutationObserverRegistration*> registrations(m_registrations); - for (HashSet<MutationObserverRegistration*>::iterator iter = registrations.begin(); iter != registrations.end(); ++iter) - MutationObserverRegistration::unregisterAndDelete(*iter); + for (auto* registration : registrations) + registration->node().unregisterMutationObserver(*registration); } -void MutationObserver::observationStarted(MutationObserverRegistration* registration) +void MutationObserver::observationStarted(MutationObserverRegistration& registration) { - ASSERT(!m_registrations.contains(registration)); - m_registrations.add(registration); + ASSERT(!m_registrations.contains(®istration)); + m_registrations.add(®istration); } -void MutationObserver::observationEnded(MutationObserverRegistration* registration) +void MutationObserver::observationEnded(MutationObserverRegistration& registration) { - ASSERT(m_registrations.contains(registration)); - m_registrations.remove(registration); + ASSERT(m_registrations.contains(®istration)); + m_registrations.remove(®istration); } typedef HashSet<RefPtr<MutationObserver>> MutationObserverSet; static MutationObserverSet& activeMutationObservers() { - DEFINE_STATIC_LOCAL(MutationObserverSet, activeObservers, ()); + static NeverDestroyed<MutationObserverSet> activeObservers; return activeObservers; } static MutationObserverSet& suspendedMutationObservers() { - DEFINE_STATIC_LOCAL(MutationObserverSet, suspendedObservers, ()); + static NeverDestroyed<MutationObserverSet> suspendedObservers; return suspendedObservers; } -void MutationObserver::enqueueMutationRecord(PassRefPtr<MutationRecord> mutation) +// https://dom.spec.whatwg.org/#signal-slot-list +static Vector<RefPtr<HTMLSlotElement>>& signalSlotList() +{ + static NeverDestroyed<Vector<RefPtr<HTMLSlotElement>>> list; + return list; +} + +static bool mutationObserverCompoundMicrotaskQueuedFlag; + +class MutationObserverMicrotask final : public Microtask { + WTF_MAKE_FAST_ALLOCATED; +private: + Result run() final + { + MutationObserver::notifyMutationObservers(); + return Result::Done; + } +}; + +static void queueMutationObserverCompoundMicrotask() +{ + if (mutationObserverCompoundMicrotaskQueuedFlag) + return; + mutationObserverCompoundMicrotaskQueuedFlag = true; + MicrotaskQueue::mainThreadQueue().append(std::make_unique<MutationObserverMicrotask>()); +} + +void MutationObserver::enqueueMutationRecord(Ref<MutationRecord>&& mutation) { ASSERT(isMainThread()); - m_records.append(mutation); + m_records.append(WTFMove(mutation)); activeMutationObservers().add(this); + + queueMutationObserverCompoundMicrotask(); +} + +void MutationObserver::enqueueSlotChangeEvent(HTMLSlotElement& slot) +{ + ASSERT(isMainThread()); + ASSERT(!signalSlotList().contains(&slot)); + signalSlotList().append(&slot); + + queueMutationObserverCompoundMicrotask(); } void MutationObserver::setHasTransientRegistration() { ASSERT(isMainThread()); activeMutationObservers().add(this); + + queueMutationObserverCompoundMicrotask(); } -HashSet<Node*> MutationObserver::getObservedNodes() const +HashSet<Node*> MutationObserver::observedNodes() const { HashSet<Node*> observedNodes; - for (HashSet<MutationObserverRegistration*>::const_iterator iter = m_registrations.begin(); iter != m_registrations.end(); ++iter) - (*iter)->addRegistrationNodesToSet(observedNodes); + for (auto* registration : m_registrations) + registration->addRegistrationNodesToSet(observedNodes); return observedNodes; } bool MutationObserver::canDeliver() { - return !m_callback->scriptExecutionContext()->activeDOMObjectsAreSuspended(); + return m_callback->canInvokeCallback(); } void MutationObserver::deliver() @@ -181,24 +219,28 @@ void MutationObserver::deliver() // Calling clearTransientRegistrations() can modify m_registrations, so it's necessary // to make a copy of the transient registrations before operating on them. Vector<MutationObserverRegistration*, 1> transientRegistrations; - for (HashSet<MutationObserverRegistration*>::iterator iter = m_registrations.begin(); iter != m_registrations.end(); ++iter) { - if ((*iter)->hasTransientRegistrations()) - transientRegistrations.append(*iter); + for (auto* registration : m_registrations) { + if (registration->hasTransientRegistrations()) + transientRegistrations.append(registration); } - for (size_t i = 0; i < transientRegistrations.size(); ++i) - transientRegistrations[i]->clearTransientRegistrations(); + for (auto& registration : transientRegistrations) + registration->clearTransientRegistrations(); if (m_records.isEmpty()) return; - Vector<RefPtr<MutationRecord>> records; + Vector<Ref<MutationRecord>> records; records.swap(m_records); m_callback->call(records, this); } -void MutationObserver::deliverAllMutations() +void MutationObserver::notifyMutationObservers() { + // https://dom.spec.whatwg.org/#notify-mutation-observers + // 1. Unset mutation observer compound microtask queued flag. + mutationObserverCompoundMicrotaskQueuedFlag = false; + ASSERT(isMainThread()); static bool deliveryInProgress = false; if (deliveryInProgress) @@ -208,29 +250,44 @@ void MutationObserver::deliverAllMutations() if (!suspendedMutationObservers().isEmpty()) { Vector<RefPtr<MutationObserver>> suspended; copyToVector(suspendedMutationObservers(), suspended); - for (size_t i = 0; i < suspended.size(); ++i) { - if (!suspended[i]->canDeliver()) + for (auto& observer : suspended) { + if (!observer->canDeliver()) continue; - suspendedMutationObservers().remove(suspended[i]); - activeMutationObservers().add(suspended[i]); + suspendedMutationObservers().remove(observer); + activeMutationObservers().add(observer); } } - while (!activeMutationObservers().isEmpty()) { - Vector<RefPtr<MutationObserver>> observers; - copyToVector(activeMutationObservers(), observers); + while (!activeMutationObservers().isEmpty() || !signalSlotList().isEmpty()) { + // 2. Let notify list be a copy of unit of related similar-origin browsing contexts' list of MutationObserver objects. + Vector<RefPtr<MutationObserver>> notifyList; + copyToVector(activeMutationObservers(), notifyList); activeMutationObservers().clear(); - std::sort(observers.begin(), observers.end(), [](const RefPtr<MutationObserver>& lhs, const RefPtr<MutationObserver>& rhs) { + std::sort(notifyList.begin(), notifyList.end(), [](auto& lhs, auto& rhs) { return lhs->m_priority < rhs->m_priority; }); - for (size_t i = 0; i < observers.size(); ++i) { - if (observers[i]->canDeliver()) - observers[i]->deliver(); + // 3. Let signalList be a copy of unit of related similar-origin browsing contexts' signal slot list. + // 4. Empty unit of related similar-origin browsing contexts' signal slot list. + Vector<RefPtr<HTMLSlotElement>> slotList; + if (!signalSlotList().isEmpty()) { + slotList.swap(signalSlotList()); + for (auto& slot : slotList) + slot->didRemoveFromSignalSlotList(); + } + + // 5. For each MutationObserver object mo in notify list, execute a compound microtask subtask + for (auto& observer : notifyList) { + if (observer->canDeliver()) + observer->deliver(); else - suspendedMutationObservers().add(observers[i]); + suspendedMutationObservers().add(observer); } + + // 6. For each slot slot in signalList, in order, fire an event named slotchange, with its bubbles attribute set to true, at slot. + for (auto& slot : slotList) + slot->dispatchSlotChangeEvent(); } deliveryInProgress = false; diff --git a/Source/WebCore/dom/MutationObserver.h b/Source/WebCore/dom/MutationObserver.h index d35294a92..b7c71bd51 100644 --- a/Source/WebCore/dom/MutationObserver.h +++ b/Source/WebCore/dom/MutationObserver.h @@ -28,31 +28,26 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef MutationObserver_h -#define MutationObserver_h +#pragma once -#include <wtf/HashMap.h> +#include "ExceptionOr.h" +#include <wtf/Forward.h> #include <wtf/HashSet.h> -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/RefPtr.h> -#include <wtf/text/AtomicString.h> -#include <wtf/text/AtomicStringHash.h> +#include <wtf/Vector.h> namespace WebCore { -class Dictionary; +class HTMLSlotElement; class MutationCallback; class MutationObserverRegistration; class MutationRecord; class Node; -typedef int ExceptionCode; - -typedef unsigned char MutationObserverOptions; -typedef unsigned char MutationRecordDeliveryOptions; +using MutationObserverOptions = unsigned char; +using MutationRecordDeliveryOptions = unsigned char; class MutationObserver : public RefCounted<MutationObserver> { + friend class MutationObserverMicrotask; public: enum MutationType { ChildList = 1 << 0, @@ -72,36 +67,45 @@ public: CharacterDataOldValue = 1 << 6, }; - static PassRefPtr<MutationObserver> create(PassRefPtr<MutationCallback>); - static void deliverAllMutations(); + static Ref<MutationObserver> create(Ref<MutationCallback>&&); ~MutationObserver(); - void observe(Node*, const Dictionary&, ExceptionCode&); - Vector<RefPtr<MutationRecord>> takeRecords(); + struct Init { + bool childList; + std::optional<bool> attributes; + std::optional<bool> characterData; + bool subtree; + std::optional<bool> attributeOldValue; + std::optional<bool> characterDataOldValue; + std::optional<Vector<String>> attributeFilter; + }; + + ExceptionOr<void> observe(Node&, const Init&); + Vector<Ref<MutationRecord>> takeRecords(); void disconnect(); - void observationStarted(MutationObserverRegistration*); - void observationEnded(MutationObserverRegistration*); - void enqueueMutationRecord(PassRefPtr<MutationRecord>); + + void observationStarted(MutationObserverRegistration&); + void observationEnded(MutationObserverRegistration&); + void enqueueMutationRecord(Ref<MutationRecord>&&); void setHasTransientRegistration(); bool canDeliver(); - HashSet<Node*> getObservedNodes() const; + HashSet<Node*> observedNodes() const; -private: - struct ObserverLessThan; + static void enqueueSlotChangeEvent(HTMLSlotElement&); - explicit MutationObserver(PassRefPtr<MutationCallback>); +private: + explicit MutationObserver(Ref<MutationCallback>&&); void deliver(); + static void notifyMutationObservers(); static bool validateOptions(MutationObserverOptions); - RefPtr<MutationCallback> m_callback; - Vector<RefPtr<MutationRecord>> m_records; + Ref<MutationCallback> m_callback; + Vector<Ref<MutationRecord>> m_records; HashSet<MutationObserverRegistration*> m_registrations; unsigned m_priority; }; -} - -#endif // MutationObserver_h +} // namespace WebCore diff --git a/Source/WebCore/dom/MutationObserver.idl b/Source/WebCore/dom/MutationObserver.idl index 9ee9792b3..f7a2c0f77 100644 --- a/Source/WebCore/dom/MutationObserver.idl +++ b/Source/WebCore/dom/MutationObserver.idl @@ -33,7 +33,17 @@ CustomIsReachable, ImplementationLacksVTable, ] interface MutationObserver { - [RaisesException] void observe(Node target, Dictionary options); + [MayThrowException] void observe(Node target, optional MutationObserverInit options); sequence<MutationRecord> takeRecords(); void disconnect(); }; + +dictionary MutationObserverInit { + boolean childList = false; + boolean attributes; + boolean characterData; + boolean subtree = false; + boolean attributeOldValue; + boolean characterDataOldValue; + sequence<DOMString> attributeFilter; +}; diff --git a/Source/WebCore/dom/MutationObserverInterestGroup.cpp b/Source/WebCore/dom/MutationObserverInterestGroup.cpp index c28d2bd93..229fcb60e 100644 --- a/Source/WebCore/dom/MutationObserverInterestGroup.cpp +++ b/Source/WebCore/dom/MutationObserverInterestGroup.cpp @@ -37,50 +37,48 @@ namespace WebCore { -PassOwnPtr<MutationObserverInterestGroup> MutationObserverInterestGroup::createIfNeeded(Node& target, MutationObserver::MutationType type, MutationRecordDeliveryOptions oldValueFlag, const QualifiedName* attributeName) +inline MutationObserverInterestGroup::MutationObserverInterestGroup(HashMap<MutationObserver*, MutationRecordDeliveryOptions>&& observers, MutationRecordDeliveryOptions oldValueFlag) + : m_observers(WTFMove(observers)) + , m_oldValueFlag(oldValueFlag) +{ + ASSERT(!m_observers.isEmpty()); +} + +std::unique_ptr<MutationObserverInterestGroup> MutationObserverInterestGroup::createIfNeeded(Node& target, MutationObserver::MutationType type, MutationRecordDeliveryOptions oldValueFlag, const QualifiedName* attributeName) { ASSERT((type == MutationObserver::Attributes && attributeName) || !attributeName); - HashMap<MutationObserver*, MutationRecordDeliveryOptions> observers; - target.getRegisteredMutationObserversOfType(observers, type, attributeName); + auto observers = target.registeredMutationObservers(type, attributeName); if (observers.isEmpty()) return nullptr; - return adoptPtr(new MutationObserverInterestGroup(observers, oldValueFlag)); -} - -MutationObserverInterestGroup::MutationObserverInterestGroup(HashMap<MutationObserver*, MutationRecordDeliveryOptions>& observers, MutationRecordDeliveryOptions oldValueFlag) - : m_oldValueFlag(oldValueFlag) -{ - ASSERT(!observers.isEmpty()); - m_observers.swap(observers); + return std::make_unique<MutationObserverInterestGroup>(WTFMove(observers), oldValueFlag); } -bool MutationObserverInterestGroup::isOldValueRequested() +bool MutationObserverInterestGroup::isOldValueRequested() const { - for (HashMap<MutationObserver*, MutationRecordDeliveryOptions>::iterator iter = m_observers.begin(); iter != m_observers.end(); ++iter) { - if (hasOldValue(iter->value)) + for (auto options : m_observers.values()) { + if (hasOldValue(options)) return true; } return false; } -void MutationObserverInterestGroup::enqueueMutationRecord(PassRefPtr<MutationRecord> prpMutation) +void MutationObserverInterestGroup::enqueueMutationRecord(Ref<MutationRecord>&& mutation) { - RefPtr<MutationRecord> mutation = prpMutation; RefPtr<MutationRecord> mutationWithNullOldValue; - for (HashMap<MutationObserver*, MutationRecordDeliveryOptions>::iterator iter = m_observers.begin(); iter != m_observers.end(); ++iter) { - MutationObserver* observer = iter->key; - if (hasOldValue(iter->value)) { - observer->enqueueMutationRecord(mutation); + for (auto& observerOptionsPair : m_observers) { + MutationObserver* observer = observerOptionsPair.key; + if (hasOldValue(observerOptionsPair.value)) { + observer->enqueueMutationRecord(mutation.copyRef()); continue; } if (!mutationWithNullOldValue) { if (mutation->oldValue().isNull()) - mutationWithNullOldValue = mutation; + mutationWithNullOldValue = mutation.ptr(); else - mutationWithNullOldValue = MutationRecord::createWithNullOldValue(mutation).get(); + mutationWithNullOldValue = MutationRecord::createWithNullOldValue(mutation).ptr(); } - observer->enqueueMutationRecord(mutationWithNullOldValue); + observer->enqueueMutationRecord(*mutationWithNullOldValue); } } diff --git a/Source/WebCore/dom/MutationObserverInterestGroup.h b/Source/WebCore/dom/MutationObserverInterestGroup.h index 84cef9920..00d3849e3 100644 --- a/Source/WebCore/dom/MutationObserverInterestGroup.h +++ b/Source/WebCore/dom/MutationObserverInterestGroup.h @@ -28,20 +28,23 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef MutationObserverInterestGroup_h -#define MutationObserverInterestGroup_h +#pragma once #include "Document.h" #include "MutationObserver.h" -#include "QualifiedName.h" +#include <memory> #include <wtf/HashMap.h> -#include <wtf/PassOwnPtr.h> namespace WebCore { +class QualifiedName; + class MutationObserverInterestGroup { + WTF_MAKE_FAST_ALLOCATED; public: - static PassOwnPtr<MutationObserverInterestGroup> createForChildListMutation(Node& target) + MutationObserverInterestGroup(HashMap<MutationObserver*, MutationRecordDeliveryOptions>&&, MutationRecordDeliveryOptions); + + static std::unique_ptr<MutationObserverInterestGroup> createForChildListMutation(Node& target) { if (!target.document().hasMutationObserversOfType(MutationObserver::ChildList)) return nullptr; @@ -50,7 +53,7 @@ public: return createIfNeeded(target, MutationObserver::ChildList, oldValueFlag); } - static PassOwnPtr<MutationObserverInterestGroup> createForCharacterDataMutation(Node& target) + static std::unique_ptr<MutationObserverInterestGroup> createForCharacterDataMutation(Node& target) { if (!target.document().hasMutationObserversOfType(MutationObserver::CharacterData)) return nullptr; @@ -58,7 +61,7 @@ public: return createIfNeeded(target, MutationObserver::CharacterData, MutationObserver::CharacterDataOldValue); } - static PassOwnPtr<MutationObserverInterestGroup> createForAttributesMutation(Node& target, const QualifiedName& attributeName) + static std::unique_ptr<MutationObserverInterestGroup> createForAttributesMutation(Node& target, const QualifiedName& attributeName) { if (!target.document().hasMutationObserversOfType(MutationObserver::Attributes)) return nullptr; @@ -66,19 +69,16 @@ public: return createIfNeeded(target, MutationObserver::Attributes, MutationObserver::AttributeOldValue, &attributeName); } - bool isOldValueRequested(); - void enqueueMutationRecord(PassRefPtr<MutationRecord>); + bool isOldValueRequested() const; + void enqueueMutationRecord(Ref<MutationRecord>&&); private: - static PassOwnPtr<MutationObserverInterestGroup> createIfNeeded(Node& target, MutationObserver::MutationType, MutationRecordDeliveryOptions oldValueFlag, const QualifiedName* attributeName = 0); - MutationObserverInterestGroup(HashMap<MutationObserver*, MutationRecordDeliveryOptions>& observers, MutationRecordDeliveryOptions oldValueFlag); + static std::unique_ptr<MutationObserverInterestGroup> createIfNeeded(Node& target, MutationObserver::MutationType, MutationRecordDeliveryOptions oldValueFlag, const QualifiedName* attributeName = nullptr); - bool hasOldValue(MutationRecordDeliveryOptions options) { return options & m_oldValueFlag; } + bool hasOldValue(MutationRecordDeliveryOptions options) const { return options & m_oldValueFlag; } HashMap<MutationObserver*, MutationRecordDeliveryOptions> m_observers; MutationRecordDeliveryOptions m_oldValueFlag; }; -} - -#endif // MutationObserverInterestGroup_h +} // namespace WebCore diff --git a/Source/WebCore/dom/MutationObserverRegistration.cpp b/Source/WebCore/dom/MutationObserverRegistration.cpp index 12e0b441f..7705f64db 100644 --- a/Source/WebCore/dom/MutationObserverRegistration.cpp +++ b/Source/WebCore/dom/MutationObserverRegistration.cpp @@ -33,27 +33,23 @@ #include "MutationObserverRegistration.h" #include "Document.h" +#include "QualifiedName.h" namespace WebCore { -PassOwnPtr<MutationObserverRegistration> MutationObserverRegistration::create(PassRefPtr<MutationObserver> observer, Node* registrationNode, MutationObserverOptions options, const HashSet<AtomicString>& attributeFilter) -{ - return adoptPtr(new MutationObserverRegistration(observer, registrationNode, options, attributeFilter)); -} - -MutationObserverRegistration::MutationObserverRegistration(PassRefPtr<MutationObserver> observer, Node* registrationNode, MutationObserverOptions options, const HashSet<AtomicString>& attributeFilter) +MutationObserverRegistration::MutationObserverRegistration(MutationObserver& observer, Node& node, MutationObserverOptions options, const HashSet<AtomicString>& attributeFilter) : m_observer(observer) - , m_registrationNode(registrationNode) + , m_node(node) , m_options(options) , m_attributeFilter(attributeFilter) { - m_observer->observationStarted(this); + m_observer->observationStarted(*this); } MutationObserverRegistration::~MutationObserverRegistration() { clearTransientRegistrations(); - m_observer->observationEnded(this); + m_observer->observationEnded(*this); } void MutationObserverRegistration::resetObservation(MutationObserverOptions options, const HashSet<AtomicString>& attributeFilter) @@ -63,53 +59,46 @@ void MutationObserverRegistration::resetObservation(MutationObserverOptions opti m_attributeFilter = attributeFilter; } -void MutationObserverRegistration::observedSubtreeNodeWillDetach(Node* node) +void MutationObserverRegistration::observedSubtreeNodeWillDetach(Node& node) { if (!isSubtree()) return; - node->registerTransientMutationObserver(this); + node.registerTransientMutationObserver(*this); m_observer->setHasTransientRegistration(); if (!m_transientRegistrationNodes) { - m_transientRegistrationNodes = adoptPtr(new NodeHashSet); + m_transientRegistrationNodes = std::make_unique<HashSet<RefPtr<Node>>>(); - ASSERT(!m_registrationNodeKeepAlive); - m_registrationNodeKeepAlive = m_registrationNode; // Balanced in clearTransientRegistrations. + ASSERT(!m_nodeKeptAlive); + m_nodeKeptAlive = &m_node; // Balanced in clearTransientRegistrations. } - m_transientRegistrationNodes->add(node); + m_transientRegistrationNodes->add(&node); } void MutationObserverRegistration::clearTransientRegistrations() { if (!m_transientRegistrationNodes) { - ASSERT(!m_registrationNodeKeepAlive); + ASSERT(!m_nodeKeptAlive); return; } - for (NodeHashSet::iterator iter = m_transientRegistrationNodes->begin(); iter != m_transientRegistrationNodes->end(); ++iter) - (*iter)->unregisterTransientMutationObserver(this); + for (auto& node : *m_transientRegistrationNodes) + node->unregisterTransientMutationObserver(*this); - m_transientRegistrationNodes.clear(); + m_transientRegistrationNodes = nullptr; - ASSERT(m_registrationNodeKeepAlive); - m_registrationNodeKeepAlive = 0; // Balanced in observeSubtreeNodeWillDetach. -} - -void MutationObserverRegistration::unregisterAndDelete(MutationObserverRegistration* registry) -{ - RefPtr<Node> registrationNode(registry->m_registrationNode); - registrationNode->unregisterMutationObserver(registry); - // The above line will cause registry to be deleted, so don't do any more in this function. + ASSERT(m_nodeKeptAlive); + m_nodeKeptAlive = nullptr; // Balanced in observeSubtreeNodeWillDetach. } -bool MutationObserverRegistration::shouldReceiveMutationFrom(Node* node, MutationObserver::MutationType type, const QualifiedName* attributeName) const +bool MutationObserverRegistration::shouldReceiveMutationFrom(Node& node, MutationObserver::MutationType type, const QualifiedName* attributeName) const { ASSERT((type == MutationObserver::Attributes && attributeName) || !attributeName); if (!(m_options & type)) return false; - if (m_registrationNode != node && !isSubtree()) + if (&m_node != &node && !isSubtree()) return false; if (type != MutationObserver::Attributes || !(m_options & MutationObserver::AttributeFilter)) @@ -123,11 +112,11 @@ bool MutationObserverRegistration::shouldReceiveMutationFrom(Node* node, Mutatio void MutationObserverRegistration::addRegistrationNodesToSet(HashSet<Node*>& nodes) const { - nodes.add(m_registrationNode); + nodes.add(&m_node); if (!m_transientRegistrationNodes) return; - for (NodeHashSet::const_iterator iter = m_transientRegistrationNodes->begin(); iter != m_transientRegistrationNodes->end(); ++iter) - nodes.add(iter->get()); + for (auto& node : *m_transientRegistrationNodes) + nodes.add(node.get()); } } // namespace WebCore diff --git a/Source/WebCore/dom/MutationObserverRegistration.h b/Source/WebCore/dom/MutationObserverRegistration.h index 5af6c357e..7b6483f47 100644 --- a/Source/WebCore/dom/MutationObserverRegistration.h +++ b/Source/WebCore/dom/MutationObserverRegistration.h @@ -28,8 +28,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef MutationObserverRegistration_h -#define MutationObserverRegistration_h +#pragma once #include "MutationObserver.h" #include <wtf/HashSet.h> @@ -41,38 +40,33 @@ namespace WebCore { class QualifiedName; class MutationObserverRegistration { + WTF_MAKE_FAST_ALLOCATED; public: - static PassOwnPtr<MutationObserverRegistration> create(PassRefPtr<MutationObserver>, Node*, MutationObserverOptions, const HashSet<AtomicString>& attributeFilter); + MutationObserverRegistration(MutationObserver&, Node&, MutationObserverOptions, const HashSet<AtomicString>& attributeFilter); ~MutationObserverRegistration(); void resetObservation(MutationObserverOptions, const HashSet<AtomicString>& attributeFilter); - void observedSubtreeNodeWillDetach(Node*); + void observedSubtreeNodeWillDetach(Node&); void clearTransientRegistrations(); bool hasTransientRegistrations() const { return m_transientRegistrationNodes && !m_transientRegistrationNodes->isEmpty(); } - static void unregisterAndDelete(MutationObserverRegistration*); - bool shouldReceiveMutationFrom(Node*, MutationObserver::MutationType, const QualifiedName* attributeName) const; + bool shouldReceiveMutationFrom(Node&, MutationObserver::MutationType, const QualifiedName* attributeName) const; bool isSubtree() const { return m_options & MutationObserver::Subtree; } - MutationObserver* observer() const { return m_observer.get(); } + MutationObserver& observer() { return m_observer.get(); } + Node& node() { return m_node; } MutationRecordDeliveryOptions deliveryOptions() const { return m_options & (MutationObserver::AttributeOldValue | MutationObserver::CharacterDataOldValue); } MutationObserverOptions mutationTypes() const { return m_options & MutationObserver::AllMutationTypes; } void addRegistrationNodesToSet(HashSet<Node*>&) const; private: - MutationObserverRegistration(PassRefPtr<MutationObserver>, Node*, MutationObserverOptions, const HashSet<AtomicString>& attributeFilter); - - RefPtr<MutationObserver> m_observer; - Node* m_registrationNode; - RefPtr<Node> m_registrationNodeKeepAlive; - typedef HashSet<RefPtr<Node>> NodeHashSet; - OwnPtr<NodeHashSet> m_transientRegistrationNodes; - + Ref<MutationObserver> m_observer; + Node& m_node; + RefPtr<Node> m_nodeKeptAlive; + std::unique_ptr<HashSet<RefPtr<Node>>> m_transientRegistrationNodes; MutationObserverOptions m_options; HashSet<AtomicString> m_attributeFilter; }; } // namespace WebCore - -#endif // MutationObserverRegistration_h diff --git a/Source/WebCore/dom/MutationRecord.cpp b/Source/WebCore/dom/MutationRecord.cpp index adbf0abb2..22955e56b 100644 --- a/Source/WebCore/dom/MutationRecord.cpp +++ b/Source/WebCore/dom/MutationRecord.cpp @@ -33,6 +33,7 @@ #include "CharacterData.h" #include "StaticNodeList.h" +#include <wtf/NeverDestroyed.h> #include <wtf/StdLibExtras.h> namespace WebCore { @@ -41,22 +42,22 @@ namespace { class ChildListRecord : public MutationRecord { public: - ChildListRecord(ContainerNode& target, PassRefPtr<NodeList> added, PassRefPtr<NodeList> removed, PassRefPtr<Node> previousSibling, PassRefPtr<Node> nextSibling) + ChildListRecord(ContainerNode& target, Ref<NodeList>&& added, Ref<NodeList>&& removed, RefPtr<Node>&& previousSibling, RefPtr<Node>&& nextSibling) : m_target(target) - , m_addedNodes(added) - , m_removedNodes(removed) - , m_previousSibling(previousSibling) - , m_nextSibling(nextSibling) + , m_addedNodes(WTFMove(added)) + , m_removedNodes(WTFMove(removed)) + , m_previousSibling(WTFMove(previousSibling)) + , m_nextSibling(WTFMove(nextSibling)) { } private: - virtual const AtomicString& type() override; - virtual Node* target() override { return &m_target.get(); } - virtual NodeList* addedNodes() override { return m_addedNodes.get(); } - virtual NodeList* removedNodes() override { return m_removedNodes.get(); } - virtual Node* previousSibling() override { return m_previousSibling.get(); } - virtual Node* nextSibling() override { return m_nextSibling.get(); } + const AtomicString& type() override; + Node* target() override { return m_target.ptr(); } + NodeList* addedNodes() override { return m_addedNodes.get(); } + NodeList* removedNodes() override { return m_removedNodes.get(); } + Node* previousSibling() override { return m_previousSibling.get(); } + Node* nextSibling() override { return m_nextSibling.get(); } Ref<ContainerNode> m_target; RefPtr<NodeList> m_addedNodes; @@ -74,15 +75,15 @@ public: } private: - virtual Node* target() override { return &m_target.get(); } - virtual String oldValue() override { return m_oldValue; } - virtual NodeList* addedNodes() override { return lazilyInitializeEmptyNodeList(m_addedNodes); } - virtual NodeList* removedNodes() override { return lazilyInitializeEmptyNodeList(m_removedNodes); } + Node* target() override { return m_target.ptr(); } + String oldValue() override { return m_oldValue; } + NodeList* addedNodes() override { return lazilyInitializeEmptyNodeList(m_addedNodes); } + NodeList* removedNodes() override { return lazilyInitializeEmptyNodeList(m_removedNodes); } static NodeList* lazilyInitializeEmptyNodeList(RefPtr<NodeList>& nodeList) { if (!nodeList) - nodeList = StaticNodeList::createEmpty(); + nodeList = StaticNodeList::create(); return nodeList.get(); } @@ -102,9 +103,9 @@ public: } private: - virtual const AtomicString& type() override; - virtual const AtomicString& attributeName() override { return m_attributeName; } - virtual const AtomicString& attributeNamespace() override { return m_attributeNamespace; } + const AtomicString& type() override; + const AtomicString& attributeName() override { return m_attributeName; } + const AtomicString& attributeNamespace() override { return m_attributeNamespace; } AtomicString m_attributeName; AtomicString m_attributeNamespace; @@ -118,69 +119,69 @@ public: } private: - virtual const AtomicString& type() override; + const AtomicString& type() override; }; class MutationRecordWithNullOldValue : public MutationRecord { public: - MutationRecordWithNullOldValue(PassRefPtr<MutationRecord> record) + MutationRecordWithNullOldValue(MutationRecord& record) : m_record(record) { } private: - virtual const AtomicString& type() override { return m_record->type(); } - virtual Node* target() override { return m_record->target(); } - virtual NodeList* addedNodes() override { return m_record->addedNodes(); } - virtual NodeList* removedNodes() override { return m_record->removedNodes(); } - virtual Node* previousSibling() override { return m_record->previousSibling(); } - virtual Node* nextSibling() override { return m_record->nextSibling(); } - virtual const AtomicString& attributeName() override { return m_record->attributeName(); } - virtual const AtomicString& attributeNamespace() override { return m_record->attributeNamespace(); } - - virtual String oldValue() override { return String(); } - - RefPtr<MutationRecord> m_record; + const AtomicString& type() override { return m_record->type(); } + Node* target() override { return m_record->target(); } + NodeList* addedNodes() override { return m_record->addedNodes(); } + NodeList* removedNodes() override { return m_record->removedNodes(); } + Node* previousSibling() override { return m_record->previousSibling(); } + Node* nextSibling() override { return m_record->nextSibling(); } + const AtomicString& attributeName() override { return m_record->attributeName(); } + const AtomicString& attributeNamespace() override { return m_record->attributeNamespace(); } + + String oldValue() override { return String(); } + + Ref<MutationRecord> m_record; }; const AtomicString& ChildListRecord::type() { - DEFINE_STATIC_LOCAL(AtomicString, childList, ("childList", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<AtomicString> childList("childList", AtomicString::ConstructFromLiteral); return childList; } const AtomicString& AttributesRecord::type() { - DEFINE_STATIC_LOCAL(AtomicString, attributes, ("attributes", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<AtomicString> attributes("attributes", AtomicString::ConstructFromLiteral); return attributes; } const AtomicString& CharacterDataRecord::type() { - DEFINE_STATIC_LOCAL(AtomicString, characterData, ("characterData", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<AtomicString> characterData("characterData", AtomicString::ConstructFromLiteral); return characterData; } } // namespace -PassRefPtr<MutationRecord> MutationRecord::createChildList(ContainerNode& target, PassRefPtr<NodeList> added, PassRefPtr<NodeList> removed, PassRefPtr<Node> previousSibling, PassRefPtr<Node> nextSibling) +Ref<MutationRecord> MutationRecord::createChildList(ContainerNode& target, Ref<NodeList>&& added, Ref<NodeList>&& removed, RefPtr<Node>&& previousSibling, RefPtr<Node>&& nextSibling) { - return adoptRef(static_cast<MutationRecord*>(new ChildListRecord(target, added, removed, previousSibling, nextSibling))); + return adoptRef(static_cast<MutationRecord&>(*new ChildListRecord(target, WTFMove(added), WTFMove(removed), WTFMove(previousSibling), WTFMove(nextSibling)))); } -PassRefPtr<MutationRecord> MutationRecord::createAttributes(Element& target, const QualifiedName& name, const AtomicString& oldValue) +Ref<MutationRecord> MutationRecord::createAttributes(Element& target, const QualifiedName& name, const AtomicString& oldValue) { - return adoptRef(static_cast<MutationRecord*>(new AttributesRecord(target, name, oldValue))); + return adoptRef(static_cast<MutationRecord&>(*new AttributesRecord(target, name, oldValue))); } -PassRefPtr<MutationRecord> MutationRecord::createCharacterData(CharacterData& target, const String& oldValue) +Ref<MutationRecord> MutationRecord::createCharacterData(CharacterData& target, const String& oldValue) { - return adoptRef(static_cast<MutationRecord*>(new CharacterDataRecord(target, oldValue))); + return adoptRef(static_cast<MutationRecord&>(*new CharacterDataRecord(target, oldValue))); } -PassRefPtr<MutationRecord> MutationRecord::createWithNullOldValue(PassRefPtr<MutationRecord> record) +Ref<MutationRecord> MutationRecord::createWithNullOldValue(MutationRecord& record) { - return adoptRef(static_cast<MutationRecord*>(new MutationRecordWithNullOldValue(record))); + return adoptRef(static_cast<MutationRecord&>(*new MutationRecordWithNullOldValue(record))); } MutationRecord::~MutationRecord() diff --git a/Source/WebCore/dom/MutationRecord.h b/Source/WebCore/dom/MutationRecord.h index 6cbb493b5..60bbfab1a 100644 --- a/Source/WebCore/dom/MutationRecord.h +++ b/Source/WebCore/dom/MutationRecord.h @@ -28,10 +28,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef MutationRecord_h -#define MutationRecord_h +#pragma once -#include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> #include <wtf/RefPtr.h> #include <wtf/text/WTFString.h> @@ -47,11 +45,11 @@ class QualifiedName; class MutationRecord : public RefCounted<MutationRecord> { public: - static PassRefPtr<MutationRecord> createChildList(ContainerNode& target, PassRefPtr<NodeList> added, PassRefPtr<NodeList> removed, PassRefPtr<Node> previousSibling, PassRefPtr<Node> nextSibling); - static PassRefPtr<MutationRecord> createAttributes(Element& target, const QualifiedName&, const AtomicString& oldValue); - static PassRefPtr<MutationRecord> createCharacterData(CharacterData& target, const String& oldValue); + static Ref<MutationRecord> createChildList(ContainerNode& target, Ref<NodeList>&& added, Ref<NodeList>&& removed, RefPtr<Node>&& previousSibling, RefPtr<Node>&& nextSibling); + static Ref<MutationRecord> createAttributes(Element& target, const QualifiedName&, const AtomicString& oldValue); + static Ref<MutationRecord> createCharacterData(CharacterData& target, const String& oldValue); - static PassRefPtr<MutationRecord> createWithNullOldValue(PassRefPtr<MutationRecord>); + static Ref<MutationRecord> createWithNullOldValue(MutationRecord&); virtual ~MutationRecord(); @@ -70,5 +68,3 @@ public: }; } // namespace WebCore - -#endif // MutationRecord_h diff --git a/Source/WebCore/dom/MutationRecord.idl b/Source/WebCore/dom/MutationRecord.idl index 5f25de392..a5e785175 100644 --- a/Source/WebCore/dom/MutationRecord.idl +++ b/Source/WebCore/dom/MutationRecord.idl @@ -39,8 +39,8 @@ readonly attribute Node previousSibling; readonly attribute Node nextSibling; - [TreatReturnedNullStringAs=Null] readonly attribute DOMString attributeName; - [TreatReturnedNullStringAs=Null] readonly attribute DOMString attributeNamespace; + readonly attribute DOMString? attributeName; + readonly attribute DOMString? attributeNamespace; - [TreatReturnedNullStringAs=Null] readonly attribute DOMString oldValue; + readonly attribute DOMString? oldValue; }; diff --git a/Source/WebCore/dom/NameNodeList.cpp b/Source/WebCore/dom/NameNodeList.cpp index e26abac46..889188941 100644 --- a/Source/WebCore/dom/NameNodeList.cpp +++ b/Source/WebCore/dom/NameNodeList.cpp @@ -2,7 +2,7 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2004, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2007, 2014 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -30,7 +30,7 @@ namespace WebCore { using namespace HTMLNames; NameNodeList::NameNodeList(ContainerNode& rootNode, const AtomicString& name) - : LiveNodeList(rootNode, NameNodeListType, InvalidateOnNameAttrChange) + : CachedLiveNodeList(rootNode, InvalidateOnNameAttrChange) , m_name(name) { } @@ -40,9 +40,4 @@ NameNodeList::~NameNodeList() ownerNode().nodeLists()->removeCacheWithAtomicName(this, m_name); } -bool NameNodeList::nodeMatches(Element* testNode) const -{ - return testNode->getNameAttribute() == m_name; -} - } // namespace WebCore diff --git a/Source/WebCore/dom/NameNodeList.h b/Source/WebCore/dom/NameNodeList.h index c7b431062..ef86c6d02 100644 --- a/Source/WebCore/dom/NameNodeList.h +++ b/Source/WebCore/dom/NameNodeList.h @@ -2,7 +2,7 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2004, 2007m 2008 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2007-2008, 2014 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -21,8 +21,7 @@ * */ -#ifndef NameNodeList_h -#define NameNodeList_h +#pragma once #include "LiveNodeList.h" #include <wtf/Forward.h> @@ -31,24 +30,27 @@ namespace WebCore { // NodeList which lists all Nodes in a Element with a given "name" attribute -class NameNodeList : public LiveNodeList { +class NameNodeList final : public CachedLiveNodeList<NameNodeList> { public: - static PassRefPtr<NameNodeList> create(ContainerNode& rootNode, Type type, const AtomicString& name) + static Ref<NameNodeList> create(ContainerNode& rootNode, const AtomicString& name) { - ASSERT_UNUSED(type, type == NameNodeListType); - return adoptRef(new NameNodeList(rootNode, name)); + return adoptRef(*new NameNodeList(rootNode, name)); } virtual ~NameNodeList(); + bool elementMatches(Element&) const override; + bool isRootedAtDocument() const override { return false; } + private: NameNodeList(ContainerNode& rootNode, const AtomicString& name); - virtual bool nodeMatches(Element*) const override; - AtomicString m_name; }; -} // namespace WebCore +inline bool NameNodeList::elementMatches(Element& element) const +{ + return element.getNameAttribute() == m_name; +} -#endif // NameNodeList_h +} // namespace WebCore diff --git a/Source/WebCore/dom/NamedFlowCollection.cpp b/Source/WebCore/dom/NamedFlowCollection.cpp index c87981671..272954811 100644 --- a/Source/WebCore/dom/NamedFlowCollection.cpp +++ b/Source/WebCore/dom/NamedFlowCollection.cpp @@ -47,11 +47,11 @@ Vector<RefPtr<WebKitNamedFlow>> NamedFlowCollection::namedFlows() { Vector<RefPtr<WebKitNamedFlow>> namedFlows; - for (NamedFlowSet::iterator it = m_namedFlows.begin(); it != m_namedFlows.end(); ++it) { - if ((*it)->flowState() == WebKitNamedFlow::FlowStateNull) + for (auto& namedFlow : m_namedFlows) { + if (namedFlow->flowState() == WebKitNamedFlow::FlowStateNull) continue; - namedFlows.append(RefPtr<WebKitNamedFlow>(*it)); + namedFlows.append(RefPtr<WebKitNamedFlow>(namedFlow)); } return namedFlows; @@ -61,27 +61,27 @@ WebKitNamedFlow* NamedFlowCollection::flowByName(const String& flowName) { NamedFlowSet::iterator it = m_namedFlows.find<String, NamedFlowHashTranslator>(flowName); if (it == m_namedFlows.end() || (*it)->flowState() == WebKitNamedFlow::FlowStateNull) - return 0; + return nullptr; return *it; } -PassRefPtr<WebKitNamedFlow> NamedFlowCollection::ensureFlowWithName(const String& flowName) +Ref<WebKitNamedFlow> NamedFlowCollection::ensureFlowWithName(const String& flowName) { NamedFlowSet::iterator it = m_namedFlows.find<String, NamedFlowHashTranslator>(flowName); if (it != m_namedFlows.end()) { WebKitNamedFlow* namedFlow = *it; ASSERT(namedFlow->flowState() == WebKitNamedFlow::FlowStateNull); - return namedFlow; + return *namedFlow; } - RefPtr<WebKitNamedFlow> newFlow = WebKitNamedFlow::create(this, flowName); + RefPtr<WebKitNamedFlow> newFlow = WebKitNamedFlow::create(*this, flowName); m_namedFlows.add(newFlow.get()); - InspectorInstrumentation::didCreateNamedFlow(document(), newFlow.get()); + InspectorInstrumentation::didCreateNamedFlow(document(), *newFlow); - return newFlow.release(); + return newFlow.releaseNonNull(); } void NamedFlowCollection::discardNamedFlow(WebKitNamedFlow* namedFlow) @@ -93,7 +93,7 @@ void NamedFlowCollection::discardNamedFlow(WebKitNamedFlow* namedFlow) ASSERT(namedFlow->flowState() == WebKitNamedFlow::FlowStateNull); ASSERT(m_namedFlows.contains(namedFlow)); - InspectorInstrumentation::willRemoveNamedFlow(document(), namedFlow); + InspectorInstrumentation::willRemoveNamedFlow(document(), *namedFlow); m_namedFlows.remove(namedFlow); } @@ -101,16 +101,17 @@ void NamedFlowCollection::discardNamedFlow(WebKitNamedFlow* namedFlow) Document* NamedFlowCollection::document() const { ScriptExecutionContext* context = ContextDestructionObserver::scriptExecutionContext(); - return toDocument(context); + return downcast<Document>(context); } -PassRefPtr<DOMNamedFlowCollection> NamedFlowCollection::createCSSOMSnapshot() +Ref<DOMNamedFlowCollection> NamedFlowCollection::createCSSOMSnapshot() { - Vector<WebKitNamedFlow*> createdFlows; - for (NamedFlowSet::iterator it = m_namedFlows.begin(); it != m_namedFlows.end(); ++it) - if ((*it)->flowState() == WebKitNamedFlow::FlowStateCreated) - createdFlows.append(*it); - return DOMNamedFlowCollection::create(createdFlows); + Vector<Ref<WebKitNamedFlow>> createdFlows; + for (auto& namedFlow : m_namedFlows) { + if (namedFlow->flowState() == WebKitNamedFlow::FlowStateCreated) + createdFlows.append(*namedFlow); + } + return DOMNamedFlowCollection::create(WTFMove(createdFlows)); } // The HashFunctions object used by the HashSet to compare between NamedFlows. diff --git a/Source/WebCore/dom/NamedFlowCollection.h b/Source/WebCore/dom/NamedFlowCollection.h index 0bb06491b..b9589d888 100644 --- a/Source/WebCore/dom/NamedFlowCollection.h +++ b/Source/WebCore/dom/NamedFlowCollection.h @@ -27,14 +27,12 @@ * SUCH DAMAGE. */ -#ifndef NamedFlowCollection_h -#define NamedFlowCollection_h +#pragma once #include "ContextDestructionObserver.h" #include "WebKitNamedFlow.h" #include <wtf/Forward.h> #include <wtf/ListHashSet.h> -#include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> #include <wtf/Vector.h> @@ -43,13 +41,13 @@ namespace WebCore { class Document; class DOMNamedFlowCollection; -class NamedFlowCollection : public RefCounted<NamedFlowCollection>, public ContextDestructionObserver { +class NamedFlowCollection final : public RefCounted<NamedFlowCollection>, public ContextDestructionObserver { public: - static PassRefPtr<NamedFlowCollection> create(Document* doc) { return adoptRef(new NamedFlowCollection(doc)); } + static Ref<NamedFlowCollection> create(Document* doc) { return adoptRef(*new NamedFlowCollection(doc)); } Vector<RefPtr<WebKitNamedFlow>> namedFlows(); WebKitNamedFlow* flowByName(const String&); - PassRefPtr<WebKitNamedFlow> ensureFlowWithName(const String&); + Ref<WebKitNamedFlow> ensureFlowWithName(const String&); void discardNamedFlow(WebKitNamedFlow*); @@ -57,13 +55,13 @@ public: virtual ~NamedFlowCollection() { } - PassRefPtr<DOMNamedFlowCollection> createCSSOMSnapshot(); + Ref<DOMNamedFlowCollection> createCSSOMSnapshot(); private: struct NamedFlowHashFunctions; struct NamedFlowHashTranslator; - typedef ListHashSet<WebKitNamedFlow*, 1, NamedFlowHashFunctions> NamedFlowSet; + typedef ListHashSet<WebKitNamedFlow*, NamedFlowHashFunctions> NamedFlowSet; explicit NamedFlowCollection(Document*); @@ -71,5 +69,3 @@ private: }; } // namespace WebCore - -#endif // NamedFlowCollection_h diff --git a/Source/WebCore/dom/NamedNodeMap.cpp b/Source/WebCore/dom/NamedNodeMap.cpp index 1b8df0f82..bcfa227a7 100644 --- a/Source/WebCore/dom/NamedNodeMap.cpp +++ b/Source/WebCore/dom/NamedNodeMap.cpp @@ -26,18 +26,14 @@ #include "NamedNodeMap.h" #include "Attr.h" -#include "Element.h" #include "ExceptionCode.h" +#include "HTMLDocument.h" +#include "HTMLElement.h" namespace WebCore { using namespace HTMLNames; -static inline bool shouldIgnoreAttributeCase(const Element& element) -{ - return element.isHTMLElement() && element.document().isHTMLDocument(); -} - void NamedNodeMap::ref() { m_element.ref(); @@ -48,61 +44,60 @@ void NamedNodeMap::deref() m_element.deref(); } -PassRefPtr<Node> NamedNodeMap::getNamedItem(const AtomicString& name) const +RefPtr<Attr> NamedNodeMap::getNamedItem(const AtomicString& name) const { return m_element.getAttributeNode(name); } -PassRefPtr<Node> NamedNodeMap::getNamedItemNS(const AtomicString& namespaceURI, const AtomicString& localName) const +RefPtr<Attr> NamedNodeMap::getNamedItemNS(const AtomicString& namespaceURI, const AtomicString& localName) const { return m_element.getAttributeNodeNS(namespaceURI, localName); } -PassRefPtr<Node> NamedNodeMap::removeNamedItem(const AtomicString& name, ExceptionCode& ec) +ExceptionOr<Ref<Attr>> NamedNodeMap::removeNamedItem(const AtomicString& name) { - unsigned index = m_element.hasAttributes() ? m_element.findAttributeIndexByName(name, shouldIgnoreAttributeCase(m_element)) : ElementData::attributeNotFound; - if (index == ElementData::attributeNotFound) { - ec = NOT_FOUND_ERR; - return 0; - } + if (!m_element.hasAttributes()) + return Exception { NOT_FOUND_ERR }; + auto index = m_element.findAttributeIndexByName(name, shouldIgnoreAttributeCase(m_element)); + if (index == ElementData::attributeNotFound) + return Exception { NOT_FOUND_ERR }; return m_element.detachAttribute(index); } -PassRefPtr<Node> NamedNodeMap::removeNamedItemNS(const AtomicString& namespaceURI, const AtomicString& localName, ExceptionCode& ec) +Vector<String> NamedNodeMap::supportedPropertyNames() const { - unsigned index = m_element.hasAttributes() ? m_element.findAttributeIndexByName(QualifiedName(nullAtom, localName, namespaceURI)) : ElementData::attributeNotFound; - if (index == ElementData::attributeNotFound) { - ec = NOT_FOUND_ERR; - return 0; + Vector<String> names = m_element.getAttributeNames(); + if (is<HTMLElement>(m_element) && is<HTMLDocument>(m_element.document())) { + names.removeAllMatching([](String& name) { + for (auto character : StringView { name }.codeUnits()) { + if (isASCIIUpper(character)) + return true; + } + return false; + }); } - return m_element.detachAttribute(index); + return names; } -PassRefPtr<Node> NamedNodeMap::setNamedItem(Node* node, ExceptionCode& ec) +ExceptionOr<Ref<Attr>> NamedNodeMap::removeNamedItemNS(const AtomicString& namespaceURI, const AtomicString& localName) { - if (!node) { - ec = NOT_FOUND_ERR; - return 0; - } - - // Not mentioned in spec: throw a HIERARCHY_REQUEST_ERROR if the user passes in a non-attribute node - if (!node->isAttributeNode()) { - ec = HIERARCHY_REQUEST_ERR; - return 0; - } - - return m_element.setAttributeNode(toAttr(node), ec); + if (!m_element.hasAttributes()) + return Exception { NOT_FOUND_ERR }; + auto index = m_element.findAttributeIndexByName(QualifiedName { nullAtom, localName, namespaceURI }); + if (index == ElementData::attributeNotFound) + return Exception { NOT_FOUND_ERR }; + return m_element.detachAttribute(index); } -PassRefPtr<Node> NamedNodeMap::setNamedItemNS(Node* node, ExceptionCode& ec) +ExceptionOr<RefPtr<Attr>> NamedNodeMap::setNamedItem(Attr& attr) { - return setNamedItem(node, ec); + return m_element.setAttributeNode(attr); } -PassRefPtr<Node> NamedNodeMap::item(unsigned index) const +RefPtr<Attr> NamedNodeMap::item(unsigned index) const { if (index >= length()) - return 0; + return nullptr; return m_element.ensureAttr(m_element.attributeAt(index).name()); } diff --git a/Source/WebCore/dom/NamedNodeMap.h b/Source/WebCore/dom/NamedNodeMap.h index fc8bfbe8b..2cb596449 100644 --- a/Source/WebCore/dom/NamedNodeMap.h +++ b/Source/WebCore/dom/NamedNodeMap.h @@ -22,60 +22,41 @@ * */ -#ifndef NamedNodeMap_h -#define NamedNodeMap_h +#pragma once +#include "ExceptionOr.h" #include "ScriptWrappable.h" -#include <wtf/PassOwnPtr.h> -#include <wtf/PassRefPtr.h> -#include <wtf/text/AtomicString.h> namespace WebCore { -class Node; +class Attr; class Element; -typedef int ExceptionCode; - class NamedNodeMap : public ScriptWrappable { WTF_MAKE_FAST_ALLOCATED; - friend class Element; public: - static PassOwnPtr<NamedNodeMap> create(Element& element) + explicit NamedNodeMap(Element& element) + : m_element(element) { - return adoptPtr(new NamedNodeMap(element)); } - void ref(); - void deref(); - - // Public DOM interface. - - PassRefPtr<Node> getNamedItem(const AtomicString&) const; - PassRefPtr<Node> removeNamedItem(const AtomicString& name, ExceptionCode&); - - PassRefPtr<Node> getNamedItemNS(const AtomicString& namespaceURI, const AtomicString& localName) const; - PassRefPtr<Node> removeNamedItemNS(const AtomicString& namespaceURI, const AtomicString& localName, ExceptionCode&); + WEBCORE_EXPORT void ref(); + WEBCORE_EXPORT void deref(); - PassRefPtr<Node> setNamedItem(Node*, ExceptionCode&); - PassRefPtr<Node> setNamedItemNS(Node*, ExceptionCode&); + WEBCORE_EXPORT unsigned length() const; + WEBCORE_EXPORT RefPtr<Attr> item(unsigned index) const; + WEBCORE_EXPORT RefPtr<Attr> getNamedItem(const AtomicString&) const; + WEBCORE_EXPORT RefPtr<Attr> getNamedItemNS(const AtomicString& namespaceURI, const AtomicString& localName) const; + WEBCORE_EXPORT ExceptionOr<RefPtr<Attr>> setNamedItem(Attr&); + WEBCORE_EXPORT ExceptionOr<Ref<Attr>> removeNamedItem(const AtomicString& name); + WEBCORE_EXPORT ExceptionOr<Ref<Attr>> removeNamedItemNS(const AtomicString& namespaceURI, const AtomicString& localName); - PassRefPtr<Node> item(unsigned index) const; - unsigned length() const; + Vector<String> supportedPropertyNames() const; - // FIXME: It's lame that the bindings generator chokes if we return Element& here. - Element* element() const { return &m_element; } + Element& element() { return m_element; } private: - explicit NamedNodeMap(Element& element) - : m_element(element) - { - // Only supports NamedNodeMaps with Element associated, DocumentType.entities and DocumentType.notations are not supported yet. - } - Element& m_element; }; } // namespace WebCore - -#endif // NamedNodeMap_h diff --git a/Source/WebCore/dom/NamedNodeMap.idl b/Source/WebCore/dom/NamedNodeMap.idl index 8f3ef898c..3635fe539 100644 --- a/Source/WebCore/dom/NamedNodeMap.idl +++ b/Source/WebCore/dom/NamedNodeMap.idl @@ -19,30 +19,17 @@ */ [ + ExportToWrappedFunction, GenerateIsReachable=ImplElementRoot, ImplementationLacksVTable, + LegacyUnenumerableNamedProperties ] interface NamedNodeMap { - - getter Node getNamedItem([Default=Undefined] optional DOMString name); - - [RaisesException] Node setNamedItem([Default=Undefined] optional Node node); - - [RaisesException] Node removeNamedItem([Default=Undefined] optional DOMString name); - - getter Node item([Default=Undefined] optional unsigned long index); - readonly attribute unsigned long length; - - - // Introduced in DOM Level 2: - - [ObjCLegacyUnnamedParameters] Node getNamedItemNS([TreatNullAs=NullString,Default=Undefined] optional DOMString namespaceURI, - [Default=Undefined] optional DOMString localName); - - [RaisesException] Node setNamedItemNS([Default=Undefined] optional Node node); - - [ObjCLegacyUnnamedParameters, RaisesException] Node removeNamedItemNS([TreatNullAs=NullString,Default=Undefined] optional DOMString namespaceURI, - [Default=Undefined] optional DOMString localName); - + getter Attr? item(unsigned long index); + getter Attr? getNamedItem(DOMString name); + Attr? getNamedItemNS(DOMString? namespaceURI, DOMString localName); + [CEReactions, MayThrowException] Attr? setNamedItem(Attr attr); + [CEReactions, MayThrowException, ImplementedAs=setNamedItem] Attr? setNamedItemNS(Attr attr); + [CEReactions, MayThrowException] Attr removeNamedItem(DOMString name); + [CEReactions, MayThrowException] Attr removeNamedItemNS(DOMString? namespaceURI, DOMString localName); }; - diff --git a/Source/WebCore/dom/NativeNodeFilter.cpp b/Source/WebCore/dom/NativeNodeFilter.cpp new file mode 100644 index 000000000..a0a34c9ff --- /dev/null +++ b/Source/WebCore/dom/NativeNodeFilter.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "NativeNodeFilter.h" + +namespace WebCore { + +NativeNodeFilter::NativeNodeFilter(RefPtr<NodeFilterCondition>&& condition) + : m_condition(condition) +{ } + +uint16_t NativeNodeFilter::acceptNode(Node* node) +{ + // cast to short silences "enumeral and non-enumeral types in return" warning + return m_condition ? m_condition->acceptNode(node) : static_cast<uint16_t>(FILTER_ACCEPT); +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/NativeNodeFilter.h b/Source/WebCore/dom/NativeNodeFilter.h new file mode 100644 index 000000000..983b5f449 --- /dev/null +++ b/Source/WebCore/dom/NativeNodeFilter.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "NodeFilter.h" +#include "NodeFilterCondition.h" +#include <wtf/RefPtr.h> + +namespace WebCore { + +class NativeNodeFilter final : public NodeFilter { +public: + static Ref<NativeNodeFilter> create(RefPtr<NodeFilterCondition>&& condition) + { + return adoptRef(*new NativeNodeFilter(WTFMove(condition))); + } + + static Ref<NativeNodeFilter> create() + { + return adoptRef(*new NativeNodeFilter()); + } + + uint16_t acceptNode(Node*) override; + + void setCondition(RefPtr<NodeFilterCondition>&& condition) { ASSERT(!m_condition); m_condition = condition; } + +private: + WEBCORE_EXPORT explicit NativeNodeFilter(RefPtr<NodeFilterCondition>&&); + + NativeNodeFilter() { } + + RefPtr<NodeFilterCondition> m_condition; +}; + +} // namespace WebCore diff --git a/Source/WebCore/dom/NoEventDispatchAssertion.h b/Source/WebCore/dom/NoEventDispatchAssertion.h new file mode 100644 index 000000000..16038611b --- /dev/null +++ b/Source/WebCore/dom/NoEventDispatchAssertion.h @@ -0,0 +1,142 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004-2016 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#pragma once + +#include "ContainerNode.h" +#include <wtf/MainThread.h> + +namespace WebCore { + +class NoEventDispatchAssertion { +public: + NoEventDispatchAssertion() + { +#if !ASSERT_DISABLED + if (!isMainThread()) + return; + ++s_count; +#endif + } + + NoEventDispatchAssertion(const NoEventDispatchAssertion&) + : NoEventDispatchAssertion() + { + } + + ~NoEventDispatchAssertion() + { +#if !ASSERT_DISABLED + if (!isMainThread()) + return; + ASSERT(s_count); + s_count--; +#endif + } + + static bool isEventAllowedInMainThread() + { +#if ASSERT_DISABLED + return true; +#else + return !isMainThread() || !s_count; +#endif + } + + static bool isEventDispatchAllowedInSubtree(Node& node) + { + return isEventAllowedInMainThread() || EventAllowedScope::isAllowedNode(node); + } + +#if !ASSERT_DISABLED + class EventAllowedScope { + public: + explicit EventAllowedScope(ContainerNode& userAgentContentRoot) + : m_eventAllowedTreeRoot(userAgentContentRoot) + , m_previousScope(s_currentScope) + { + s_currentScope = this; + } + + ~EventAllowedScope() + { + s_currentScope = m_previousScope; + } + + static bool isAllowedNode(Node& node) + { + return s_currentScope && s_currentScope->isAllowedNodeInternal(node); + } + + private: + bool isAllowedNodeInternal(Node& node) + { + return m_eventAllowedTreeRoot->contains(&node) || (m_previousScope && m_previousScope->isAllowedNodeInternal(node)); + } + + Ref<ContainerNode> m_eventAllowedTreeRoot; + + EventAllowedScope* m_previousScope; + static EventAllowedScope* s_currentScope; + }; +#else + class EventAllowedScope { + public: + explicit EventAllowedScope(ContainerNode&) { } + static bool isAllowedNode(Node&) { return true; } + }; +#endif + +#if !ASSERT_DISABLED + class DisableAssertionsInScope { + public: + DisableAssertionsInScope() + { + if (!isMainThread()) + return; + s_existingCount = s_count; + s_count = 0; + } + + ~DisableAssertionsInScope() + { + s_count = s_existingCount; + s_existingCount = 0; + } + private: + WEBCORE_EXPORT static unsigned s_existingCount; + }; +#else + class DisableAssertionsInScope { + public: + DisableAssertionsInScope() { } + }; +#endif + +#if !ASSERT_DISABLED +private: + WEBCORE_EXPORT static unsigned s_count; +#endif +}; + +} // namespace WebCore diff --git a/Source/WebCore/dom/Node.cpp b/Source/WebCore/dom/Node.cpp index 0db540bbf..0258126d3 100644 --- a/Source/WebCore/dom/Node.cpp +++ b/Source/WebCore/dom/Node.cpp @@ -2,7 +2,7 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2014 Apple Inc. All rights reserved. * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * @@ -29,76 +29,59 @@ #include "Attr.h" #include "BeforeLoadEvent.h" #include "ChildListMutationScope.h" -#include "Chrome.h" -#include "ChromeClient.h" -#include "CSSParser.h" -#include "CSSRule.h" -#include "CSSSelector.h" -#include "CSSSelectorList.h" -#include "CSSStyleRule.h" -#include "CSSStyleSheet.h" +#include "ComposedTreeAncestorIterator.h" #include "ContainerNodeAlgorithms.h" #include "ContextMenuController.h" -#include "DOMImplementation.h" +#include "DataTransfer.h" #include "DocumentType.h" #include "ElementIterator.h" #include "ElementRareData.h" +#include "ElementTraversal.h" #include "EventDispatcher.h" -#include "EventException.h" #include "EventHandler.h" -#include "FlowThreadController.h" +#include "ExceptionCode.h" #include "FrameView.h" +#include "HTMLBodyElement.h" #include "HTMLCollection.h" #include "HTMLElement.h" #include "HTMLImageElement.h" +#include "HTMLSlotElement.h" #include "HTMLStyleElement.h" -#include "InsertionPoint.h" +#include "InputEvent.h" +#include "InspectorController.h" #include "KeyboardEvent.h" #include "Logging.h" #include "MutationEvent.h" +#include "NoEventDispatchAssertion.h" #include "NodeRenderStyle.h" -#include "PlatformMouseEvent.h" -#include "PlatformWheelEvent.h" #include "ProcessingInstruction.h" #include "ProgressEvent.h" +#include "Range.h" #include "RenderBlock.h" #include "RenderBox.h" #include "RenderTextControl.h" #include "RenderView.h" #include "ScopedEventQueue.h" -#include "Settings.h" #include "StorageEvent.h" #include "StyleResolver.h" +#include "StyleSheetContents.h" #include "TemplateContentDocumentFragment.h" #include "TextEvent.h" #include "TouchEvent.h" #include "TreeScopeAdopter.h" #include "WheelEvent.h" +#include "XMLNSNames.h" #include "XMLNames.h" -#include "htmlediting.h" -#include <runtime/Operations.h> -#include <runtime/VM.h> #include <wtf/RefCountedLeakCounter.h> +#include <wtf/SHA1.h> +#include <wtf/Variant.h> #include <wtf/text/CString.h> #include <wtf/text/StringBuilder.h> -#if ENABLE(INDIE_UI) -#include "UIRequestEvent.h" -#endif - -#if ENABLE(INSPECTOR) -#include "InspectorController.h" -#endif - namespace WebCore { using namespace HTMLNames; -bool Node::isSupported(const String& feature, const String& version) -{ - return DOMImplementation::hasFeature(feature, version); -} - #if DUMP_NODE_STATISTICS static HashSet<Node*> liveNodeSet; #endif @@ -113,14 +96,11 @@ void Node::dumpStatistics() size_t textNodes = 0; size_t cdataNodes = 0; size_t commentNodes = 0; - size_t entityReferenceNodes = 0; size_t entityNodes = 0; size_t piNodes = 0; size_t documentNodes = 0; size_t docTypeNodes = 0; size_t fragmentNodes = 0; - size_t notationNodes = 0; - size_t xpathNSNodes = 0; size_t shadowRootNodes = 0; HashMap<String, size_t> perTagCount; @@ -131,14 +111,12 @@ void Node::dumpStatistics() size_t elementsWithRareData = 0; size_t elementsWithNamedNodeMap = 0; - for (HashSet<Node*>::iterator it = liveNodeSet.begin(); it != liveNodeSet.end(); ++it) { - Node* node = *it; - + for (auto* node : liveNodeSet) { if (node->hasRareData()) { ++nodesWithRareData; - if (node->isElementNode()) { + if (is<Element>(*node)) { ++elementsWithRareData; - if (toElement(node)->hasNamedNodeMap()) + if (downcast<Element>(*node).hasNamedNodeMap()) ++elementsWithNamedNodeMap; } } @@ -148,18 +126,18 @@ void Node::dumpStatistics() ++elementNodes; // Tag stats - Element* element = toElement(node); - HashMap<String, size_t>::AddResult result = perTagCount.add(element->tagName(), 1); + Element& element = downcast<Element>(*node); + HashMap<String, size_t>::AddResult result = perTagCount.add(element.tagName(), 1); if (!result.isNewEntry) result.iterator->value++; - if (ElementData* elementData = element->elementData()) { + if (const ElementData* elementData = element.elementData()) { unsigned length = elementData->length(); attributes += length; ++elementsWithAttributeStorage; for (unsigned i = 0; i < length; ++i) { - Attribute& attr = elementData->attributeAt(i); - if (attr.attr()) + const Attribute& attr = elementData->attributeAt(i); + if (!attr.isEmpty()) ++attributesWithAttr; } } @@ -181,10 +159,6 @@ void Node::dumpStatistics() ++commentNodes; break; } - case ENTITY_REFERENCE_NODE: { - ++entityReferenceNodes; - break; - } case ENTITY_NODE: { ++entityNodes; break; @@ -208,14 +182,6 @@ void Node::dumpStatistics() ++fragmentNodes; break; } - case NOTATION_NODE: { - ++notationNodes; - break; - } - case XPATH_NAMESPACE_NODE: { - ++xpathNSNodes; - break; - } } } @@ -228,19 +194,16 @@ void Node::dumpStatistics() printf(" Number of Text nodes: %zu\n", textNodes); printf(" Number of CDATASection nodes: %zu\n", cdataNodes); printf(" Number of Comment nodes: %zu\n", commentNodes); - printf(" Number of EntityReference nodes: %zu\n", entityReferenceNodes); printf(" Number of Entity nodes: %zu\n", entityNodes); printf(" Number of ProcessingInstruction nodes: %zu\n", piNodes); printf(" Number of Document nodes: %zu\n", documentNodes); printf(" Number of DocumentType nodes: %zu\n", docTypeNodes); printf(" Number of DocumentFragment nodes: %zu\n", fragmentNodes); - printf(" Number of Notation nodes: %zu\n", notationNodes); - printf(" Number of XPathNS nodes: %zu\n", xpathNSNodes); printf(" Number of ShadowRoot nodes: %zu\n", shadowRootNodes); printf("Element tag name distibution:\n"); - for (HashMap<String, size_t>::iterator it = perTagCount.begin(); it != perTagCount.end(); ++it) - printf(" Number of <%s> tags: %zu\n", it->key.utf8().data(), it->value); + for (auto& stringSizePair : perTagCount) + printf(" Number of <%s> tags: %zu\n", stringSizePair.key.utf8().data(), stringSizePair.value); printf("Attributes:\n"); printf(" Number of Attributes (non-Node and Node): %zu [%zu]\n", attributes, sizeof(Attribute)); @@ -252,10 +215,17 @@ void Node::dumpStatistics() } DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, nodeCounter, ("WebCoreNode")); -DEFINE_DEBUG_ONLY_GLOBAL(HashSet<Node*>, ignoreSet, ); #ifndef NDEBUG static bool shouldIgnoreLeaks = false; + +static HashSet<Node*>& ignoreSet() +{ + static NeverDestroyed<HashSet<Node*>> ignore; + + return ignore; +} + #endif void Node::startIgnoringLeaks() @@ -276,7 +246,7 @@ void Node::trackForDebugging() { #ifndef NDEBUG if (shouldIgnoreLeaks) - ignoreSet.add(this); + ignoreSet().add(this); else nodeCounter.increment(); #endif @@ -286,10 +256,29 @@ void Node::trackForDebugging() #endif } +Node::Node(Document& document, ConstructionType type) + : m_refCount(1) + , m_nodeFlags(type) + , m_treeScope(&document) +{ + ASSERT(isMainThread()); + + document.incrementReferencingNodeCount(); + +#if !defined(NDEBUG) || (defined(DUMP_NODE_STATISTICS) && DUMP_NODE_STATISTICS) + trackForDebugging(); +#endif +} + Node::~Node() { + ASSERT(isMainThread()); + ASSERT(!m_refCount); + ASSERT(m_deletionHasBegun); + ASSERT(!m_adoptionIsRequired); + #ifndef NDEBUG - if (!ignoreSet.remove(this)) + if (!ignoreSet().remove(this)) nodeCounter.decrement(); #endif @@ -297,7 +286,7 @@ Node::~Node() liveNodeSet.remove(this); #endif - ASSERT(!renderer()); + ASSERT_WITH_SECURITY_IMPLICATION(!renderer()); ASSERT(!parentNode()); ASSERT(!m_previous); ASSERT(!m_next); @@ -305,53 +294,42 @@ Node::~Node() if (hasRareData()) clearRareData(); - if (!isContainerNode()) { - if (Document* document = documentInternal()) - willBeDeletedFrom(document); - } + if (!isContainerNode()) + willBeDeletedFrom(document()); - m_treeScope->selfOnlyDeref(); + if (hasEventTargetData()) + clearEventTargetData(); - InspectorCounters::decrementCounter(InspectorCounters::NodeCounter); + document().decrementReferencingNodeCount(); } -void Node::willBeDeletedFrom(Document* document) +void Node::willBeDeletedFrom(Document& document) { if (hasEventTargetData()) { + document.didRemoveWheelEventHandler(*this, EventHandlerRemoval::All); #if ENABLE(TOUCH_EVENTS) && PLATFORM(IOS) - if (document) - document->removeTouchEventListener(this, true); + document.removeTouchEventListener(this, true); + document.removeTouchEventHandler(this, true); +#else + // FIXME: This should call didRemoveTouchEventHandler(). #endif - clearEventTargetData(); - } - - if (document) { - if (AXObjectCache* cache = document->existingAXObjectCache()) - cache->remove(this); } -} -NodeRareData* Node::rareData() const -{ - ASSERT_WITH_SECURITY_IMPLICATION(hasRareData()); - return static_cast<NodeRareData*>(m_data.m_rareData); + if (AXObjectCache* cache = document.existingAXObjectCache()) + cache->remove(this); } -NodeRareData& Node::ensureRareData() +void Node::materializeRareData() { - if (hasRareData()) - return *rareData(); - NodeRareData* data; - if (isElementNode()) - data = ElementRareData::create(toRenderElement(m_data.m_renderer)).leakPtr(); + if (is<Element>(*this)) + data = std::make_unique<ElementRareData>(downcast<RenderElement>(m_data.m_renderer)).release(); else - data = NodeRareData::create(m_data.m_renderer).leakPtr(); + data = std::make_unique<NodeRareData>(m_data.m_renderer).release(); ASSERT(data); m_data.m_rareData = data; setFlag(HasRareDataFlag); - return *data; } void Node::clearRareData() @@ -373,35 +351,21 @@ Node* Node::toNode() return this; } -HTMLInputElement* Node::toInputElement() -{ - // If one of the below ASSERTs trigger, you are calling this function - // directly or indirectly from a constructor or destructor of this object. - // Don't do this! - ASSERT(!(isHTMLElement() && hasTagName(inputTag))); - return 0; -} - String Node::nodeValue() const { return String(); } -void Node::setNodeValue(const String& /*nodeValue*/, ExceptionCode& ec) +ExceptionOr<void> Node::setNodeValue(const String&) { - // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly - if (isReadOnlyNode()) { - ec = NO_MODIFICATION_ALLOWED_ERR; - return; - } - // By default, setting nodeValue has no effect. + return { }; } -PassRefPtr<NodeList> Node::childNodes() +RefPtr<NodeList> Node::childNodes() { - if (isContainerNode()) - return ensureRareData().ensureNodeLists().ensureChildNodeList(toContainerNode(*this)); + if (is<ContainerNode>(*this)) + return ensureRareData().ensureNodeLists().ensureChildNodeList(downcast<ContainerNode>(*this)); return ensureRareData().ensureNodeLists().ensureEmptyChildNodeList(*this); } @@ -421,46 +385,173 @@ Node* Node::firstDescendant() const return n; } -bool Node::insertBefore(PassRefPtr<Node> newChild, Node* refChild, ExceptionCode& ec) +Element* Node::previousElementSibling() const { - if (!isContainerNode()) { - ec = HIERARCHY_REQUEST_ERR; - return false; + return ElementTraversal::previousSibling(*this); +} + +Element* Node::nextElementSibling() const +{ + return ElementTraversal::nextSibling(*this); +} + +ExceptionOr<void> Node::insertBefore(Node& newChild, Node* refChild) +{ + if (!is<ContainerNode>(*this)) + return Exception { HIERARCHY_REQUEST_ERR }; + return downcast<ContainerNode>(*this).insertBefore(newChild, refChild); +} + +ExceptionOr<void> Node::replaceChild(Node& newChild, Node& oldChild) +{ + if (!is<ContainerNode>(*this)) + return Exception { HIERARCHY_REQUEST_ERR }; + return downcast<ContainerNode>(*this).replaceChild(newChild, oldChild); +} + +ExceptionOr<void> Node::removeChild(Node& oldChild) +{ + if (!is<ContainerNode>(*this)) + return Exception { NOT_FOUND_ERR }; + return downcast<ContainerNode>(*this).removeChild(oldChild); +} + +ExceptionOr<void> Node::appendChild(Node& newChild) +{ + if (!is<ContainerNode>(*this)) + return Exception { HIERARCHY_REQUEST_ERR }; + return downcast<ContainerNode>(*this).appendChild(newChild); +} + +static HashSet<RefPtr<Node>> nodeSetPreTransformedFromNodeOrStringVector(const Vector<NodeOrString>& vector) +{ + HashSet<RefPtr<Node>> nodeSet; + for (const auto& variant : vector) { + WTF::switchOn(variant, + [&] (const RefPtr<Node>& node) { nodeSet.add(const_cast<Node*>(node.get())); }, + [] (const String&) { } + ); } - return toContainerNode(this)->insertBefore(newChild, refChild, ec); + return nodeSet; } -bool Node::replaceChild(PassRefPtr<Node> newChild, Node* oldChild, ExceptionCode& ec) +static RefPtr<Node> firstPrecedingSiblingNotInNodeSet(Node& context, const HashSet<RefPtr<Node>>& nodeSet) { - if (!isContainerNode()) { - ec = HIERARCHY_REQUEST_ERR; - return false; + for (auto* sibling = context.previousSibling(); sibling; sibling = sibling->previousSibling()) { + if (!nodeSet.contains(sibling)) + return sibling; } - return toContainerNode(this)->replaceChild(newChild, oldChild, ec); + return nullptr; } -bool Node::removeChild(Node* oldChild, ExceptionCode& ec) +static RefPtr<Node> firstFollowingSiblingNotInNodeSet(Node& context, const HashSet<RefPtr<Node>>& nodeSet) { - if (!isContainerNode()) { - ec = NOT_FOUND_ERR; - return false; + for (auto* sibling = context.nextSibling(); sibling; sibling = sibling->nextSibling()) { + if (!nodeSet.contains(sibling)) + return sibling; } - return toContainerNode(this)->removeChild(oldChild, ec); + return nullptr; } -bool Node::appendChild(PassRefPtr<Node> newChild, ExceptionCode& ec) +ExceptionOr<RefPtr<Node>> Node::convertNodesOrStringsIntoNode(Vector<NodeOrString>&& nodeOrStringVector) { - if (!isContainerNode()) { - ec = HIERARCHY_REQUEST_ERR; - return false; + if (nodeOrStringVector.isEmpty()) + return nullptr; + + Vector<Ref<Node>> nodes; + nodes.reserveInitialCapacity(nodeOrStringVector.size()); + for (auto& variant : nodeOrStringVector) { + WTF::switchOn(variant, + [&](RefPtr<Node>& node) { nodes.uncheckedAppend(*node.get()); }, + [&](String& string) { nodes.uncheckedAppend(Text::create(document(), string)); } + ); + } + + if (nodes.size() == 1) + return RefPtr<Node> { WTFMove(nodes.first()) }; + + auto nodeToReturn = DocumentFragment::create(document()); + for (auto& node : nodes) { + auto appendResult = nodeToReturn->appendChild(node); + if (appendResult.hasException()) + return appendResult.releaseException(); } - return toContainerNode(this)->appendChild(newChild, ec); + return RefPtr<Node> { WTFMove(nodeToReturn) }; } -void Node::remove(ExceptionCode& ec) +ExceptionOr<void> Node::before(Vector<NodeOrString>&& nodeOrStringVector) { - if (ContainerNode* parent = parentNode()) - parent->removeChild(this, ec); + RefPtr<ContainerNode> parent = parentNode(); + if (!parent) + return { }; + + auto nodeSet = nodeSetPreTransformedFromNodeOrStringVector(nodeOrStringVector); + auto viablePreviousSibling = firstPrecedingSiblingNotInNodeSet(*this, nodeSet); + + auto result = convertNodesOrStringsIntoNode(WTFMove(nodeOrStringVector)); + if (result.hasException()) + return result.releaseException(); + auto node = result.releaseReturnValue(); + if (!node) + return { }; + + if (viablePreviousSibling) + viablePreviousSibling = viablePreviousSibling->nextSibling(); + else + viablePreviousSibling = parent->firstChild(); + + return parent->insertBefore(*node, viablePreviousSibling.get()); +} + +ExceptionOr<void> Node::after(Vector<NodeOrString>&& nodeOrStringVector) +{ + RefPtr<ContainerNode> parent = parentNode(); + if (!parent) + return { }; + + auto nodeSet = nodeSetPreTransformedFromNodeOrStringVector(nodeOrStringVector); + auto viableNextSibling = firstFollowingSiblingNotInNodeSet(*this, nodeSet); + + auto result = convertNodesOrStringsIntoNode(WTFMove(nodeOrStringVector)); + if (result.hasException()) + return result.releaseException(); + auto node = result.releaseReturnValue(); + if (!node) + return { }; + + return parent->insertBefore(*node, viableNextSibling.get()); +} + +ExceptionOr<void> Node::replaceWith(Vector<NodeOrString>&& nodeOrStringVector) +{ + RefPtr<ContainerNode> parent = parentNode(); + if (!parent) + return { }; + + auto nodeSet = nodeSetPreTransformedFromNodeOrStringVector(nodeOrStringVector); + auto viableNextSibling = firstFollowingSiblingNotInNodeSet(*this, nodeSet); + + auto result = convertNodesOrStringsIntoNode(WTFMove(nodeOrStringVector)); + if (result.hasException()) + return result.releaseException(); + + if (parentNode() == parent) { + if (auto node = result.releaseReturnValue()) + return parent->replaceChild(*node, *this); + return parent->removeChild(*this); + } + + if (auto node = result.releaseReturnValue()) + return parent->insertBefore(*node, viableNextSibling.get()); + return { }; +} + +ExceptionOr<void> Node::remove() +{ + auto* parent = parentNode(); + if (!parent) + return { }; + return parent->removeChild(*this); } void Node::normalize() @@ -474,23 +565,23 @@ void Node::normalize() while (node) { NodeType type = node->nodeType(); if (type == ELEMENT_NODE) - toElement(node.get())->normalizeAttributes(); + downcast<Element>(*node).normalizeAttributes(); if (node == this) break; if (type != TEXT_NODE) { - node = NodeTraversal::nextPostOrder(node.get()); + node = NodeTraversal::nextPostOrder(*node); continue; } - RefPtr<Text> text = toText(node.get()); + RefPtr<Text> text = downcast<Text>(node.get()); // Remove empty text nodes. if (!text->length()) { // Care must be taken to get the next node before removing the current node. - node = NodeTraversal::nextPostOrder(node.get()); - text->remove(IGNORE_EXCEPTION); + node = NodeTraversal::nextPostOrder(*node); + text->remove(); continue; } @@ -498,37 +589,44 @@ void Node::normalize() while (Node* nextSibling = node->nextSibling()) { if (nextSibling->nodeType() != TEXT_NODE) break; - RefPtr<Text> nextText = toText(nextSibling); + RefPtr<Text> nextText = downcast<Text>(nextSibling); // Remove empty text nodes. if (!nextText->length()) { - nextText->remove(IGNORE_EXCEPTION); + nextText->remove(); continue; } // Both non-empty text nodes. Merge them. unsigned offset = text->length(); - text->appendData(nextText->data(), IGNORE_EXCEPTION); + text->appendData(nextText->data()); document().textNodesMerged(nextText.get(), offset); - nextText->remove(IGNORE_EXCEPTION); + nextText->remove(); } - node = NodeTraversal::nextPostOrder(node.get()); + node = NodeTraversal::nextPostOrder(*node); } } +ExceptionOr<Ref<Node>> Node::cloneNodeForBindings(bool deep) +{ + if (UNLIKELY(isShadowRoot())) + return Exception { NOT_SUPPORTED_ERR }; + return cloneNode(deep); +} + const AtomicString& Node::prefix() const { // For nodes other than elements and attributes, the prefix is always null return nullAtom; } -void Node::setPrefix(const AtomicString& /*prefix*/, ExceptionCode& ec) +ExceptionOr<void> Node::setPrefix(const AtomicString&) { // The spec says that for nodes other than elements and attributes, prefix is always null. // It does not say what to do when the user tries to set the prefix on another type of // node, however Mozilla throws a NAMESPACE_ERR exception. - ec = NAMESPACE_ERR; + return Exception { NAMESPACE_ERR }; } const AtomicString& Node::localName() const @@ -541,42 +639,30 @@ const AtomicString& Node::namespaceURI() const return nullAtom; } -bool Node::isContentEditable(UserSelectAllTreatment treatment) +bool Node::isContentEditable() { - document().updateStyleIfNeeded(); - return hasEditableStyle(Editable, treatment); + return computeEditability(UserSelectAllDoesNotAffectEditability, ShouldUpdateStyle::Update) != Editability::ReadOnly; } bool Node::isContentRichlyEditable() { - document().updateStyleIfNeeded(); - return hasEditableStyle(RichlyEditable, UserSelectAllIsAlwaysNonEditable); + return computeEditability(UserSelectAllIsAlwaysNonEditable, ShouldUpdateStyle::Update) == Editability::CanEditRichly; } void Node::inspect() { -#if ENABLE(INSPECTOR) if (document().page()) document().page()->inspectorController().inspect(this); -#endif } -bool Node::hasEditableStyle(EditableLevel editableLevel, UserSelectAllTreatment treatment) const +static Node::Editability computeEditabilityFromComputedStyle(const Node& startNode, Node::UserSelectAllTreatment treatment) { - if (!document().hasLivingRenderTree()) - return false; - if (document().frame() && document().frame()->page() && document().frame()->page()->isEditable() && !containingShadowRoot()) - return true; - - if (isPseudoElement()) - return false; - // Ideally we'd call ASSERT(!needsStyleRecalc()) here, but - // ContainerNode::setFocus() calls setNeedsStyleRecalc(), so the assertion + // ContainerNode::setFocus() calls invalidateStyleForSubtree(), so the assertion // would fire in the middle of Document::setFocusedElement(). - for (const Node* node = this; node; node = node->parentNode()) { - RenderStyle* style = node->isDocumentNode() ? node->renderStyle() : const_cast<Node*>(node)->computedStyle(); + for (const Node* node = &startNode; node; node = node->parentNode()) { + auto* style = node->isDocumentNode() ? node->renderStyle() : const_cast<Node*>(node)->computedStyle(); if (!style) continue; if (style->display() == NONE) @@ -584,60 +670,54 @@ bool Node::hasEditableStyle(EditableLevel editableLevel, UserSelectAllTreatment #if ENABLE(USERSELECT_ALL) // Elements with user-select: all style are considered atomic // therefore non editable. - if (treatment == UserSelectAllIsAlwaysNonEditable && style->userSelect() == SELECT_ALL) - return false; + if (treatment == Node::UserSelectAllIsAlwaysNonEditable && style->userSelect() == SELECT_ALL) + return Node::Editability::ReadOnly; #else UNUSED_PARAM(treatment); #endif switch (style->userModify()) { case READ_ONLY: - return false; + return Node::Editability::ReadOnly; case READ_WRITE: - return true; + return Node::Editability::CanEditRichly; case READ_WRITE_PLAINTEXT_ONLY: - return editableLevel != RichlyEditable; + return Node::Editability::CanEditPlainText; } ASSERT_NOT_REACHED(); - return false; + return Node::Editability::ReadOnly; } - return false; + return Node::Editability::ReadOnly; } -bool Node::isEditableToAccessibility(EditableLevel editableLevel) const +Node::Editability Node::computeEditability(UserSelectAllTreatment treatment, ShouldUpdateStyle shouldUpdateStyle) const { - if (hasEditableStyle(editableLevel)) - return true; + if (!document().hasLivingRenderTree() || isPseudoElement()) + return Editability::ReadOnly; - // FIXME: Respect editableLevel for ARIA editable elements. - if (editableLevel == RichlyEditable) - return false; - - ASSERT(AXObjectCache::accessibilityEnabled()); - ASSERT(document().existingAXObjectCache()); + if (isInShadowTree()) + return HTMLElement::editabilityFromContentEditableAttr(*this); - if (AXObjectCache* cache = document().existingAXObjectCache()) - return cache->rootAXEditableElement(this); + if (document().frame() && document().frame()->page() && document().frame()->page()->isEditable()) + return Editability::CanEditRichly; - return false; + if (shouldUpdateStyle == ShouldUpdateStyle::Update && document().needsStyleRecalc()) { + if (!document().usesStyleBasedEditability()) + return HTMLElement::editabilityFromContentEditableAttr(*this); + document().updateStyleIfNeeded(); + } + return computeEditabilityFromComputedStyle(*this, treatment); } RenderBox* Node::renderBox() const { RenderObject* renderer = this->renderer(); - return renderer && renderer->isBox() ? toRenderBox(renderer) : 0; + return is<RenderBox>(renderer) ? downcast<RenderBox>(renderer) : nullptr; } RenderBoxModelObject* Node::renderBoxModelObject() const { RenderObject* renderer = this->renderer(); - return renderer && renderer->isBoxModelObject() ? toRenderBoxModelObject(renderer) : 0; -} - -LayoutRect Node::boundingBox() const -{ - if (renderer()) - return renderer()->absoluteBoundingBoxRect(); - return LayoutRect(); + return is<RenderBoxModelObject>(renderer) ? downcast<RenderBoxModelObject>(renderer) : nullptr; } LayoutRect Node::renderRect(bool* isReplaced) @@ -645,7 +725,7 @@ LayoutRect Node::renderRect(bool* isReplaced) RenderObject* hitRenderer = this->renderer(); ASSERT(hitRenderer); RenderObject* renderer = hitRenderer; - while (renderer && !renderer->isBody() && !renderer->isRoot()) { + while (renderer && !renderer->isBody() && !renderer->isDocumentElementRenderer()) { if (renderer->isRenderBlock() || renderer->isInlineBlockOrInlineTable() || renderer->isReplaced()) { *isReplaced = renderer->isReplaced(); return renderer->absoluteBoundingBoxRect(); @@ -655,16 +735,6 @@ LayoutRect Node::renderRect(bool* isReplaced) return LayoutRect(); } -void Node::markAncestorsWithChildNeedsStyleRecalc() -{ - ContainerNode* ancestor = isPseudoElement() ? toPseudoElement(this)->hostElement() : parentOrShadowHostNode(); - for (; ancestor && !ancestor->childNeedsStyleRecalc(); ancestor = ancestor->parentOrShadowHostNode()) - ancestor->setChildNeedsStyleRecalc(); - - if (document().childNeedsStyleRecalc()) - document().scheduleStyleRecalc(); -} - void Node::refEventTarget() { ref(); @@ -675,26 +745,70 @@ void Node::derefEventTarget() deref(); } -void Node::setNeedsStyleRecalc(StyleChangeType changeType) +void Node::adjustStyleValidity(Style::Validity validity, Style::InvalidationMode mode) +{ + if (validity > styleValidity()) { + m_nodeFlags &= ~StyleValidityMask; + m_nodeFlags |= static_cast<unsigned>(validity) << StyleValidityShift; + } + if (mode == Style::InvalidationMode::RecompositeLayer) + setFlag(StyleResolutionShouldRecompositeLayerFlag); +} + +inline void Node::updateAncestorsForStyleRecalc() +{ + auto composedAncestors = composedTreeAncestors(*this); + auto it = composedAncestors.begin(); + auto end = composedAncestors.end(); + if (it != end) { + it->setDirectChildNeedsStyleRecalc(); + + if (it->childrenAffectedByPropertyBasedBackwardPositionalRules()) + it->adjustStyleValidity(Style::Validity::SubtreeInvalid, Style::InvalidationMode::Normal); + + for (; it != end; ++it) { + // Iterator skips over shadow roots. + if (auto* shadowRoot = it->shadowRoot()) + shadowRoot->setChildNeedsStyleRecalc(); + if (it->childNeedsStyleRecalc()) + break; + it->setChildNeedsStyleRecalc(); + } + } + + auto* documentElement = document().documentElement(); + if (!documentElement) + return; + if (!documentElement->childNeedsStyleRecalc() && !documentElement->needsStyleRecalc()) + return; + document().setChildNeedsStyleRecalc(); + document().scheduleStyleRecalc(); +} + +void Node::invalidateStyle(Style::Validity validity, Style::InvalidationMode mode) { - ASSERT(changeType != NoStyleChange); + ASSERT(validity != Style::Validity::Valid); if (!inRenderedDocument()) return; - StyleChangeType existingChangeType = styleChangeType(); - if (changeType > existingChangeType) - setStyleChange(changeType); + // FIXME: This should eventually be an ASSERT. + if (document().inRenderTreeUpdate()) + return; + + // FIXME: Why the second condition? + bool markAncestors = styleValidity() == Style::Validity::Valid || validity == Style::Validity::SubtreeAndRenderersInvalid; + + adjustStyleValidity(validity, mode); - if (existingChangeType == NoStyleChange || changeType == ReconstructRenderTree) - markAncestorsWithChildNeedsStyleRecalc(); + if (markAncestors) + updateAncestorsForStyleRecalc(); } -unsigned Node::nodeIndex() const +unsigned Node::computeNodeIndex() const { - Node *_tempNode = previousSibling(); - unsigned count=0; - for ( count=0; _tempNode; count++ ) - _tempNode = _tempNode->previousSibling(); + unsigned count = 0; + for (Node* sibling = previousSibling(); sibling; sibling = sibling->previousSibling()) + ++count; return count; } @@ -727,10 +841,15 @@ bool Document::shouldInvalidateNodeListAndCollectionCaches(const QualifiedName* void Document::invalidateNodeListAndCollectionCaches(const QualifiedName* attrName) { - for (HashSet<LiveNodeList*>::iterator it = m_listsInvalidatedAtDocument.begin(), end = m_listsInvalidatedAtDocument.end(); it != end; ++it) - (*it)->invalidateCache(attrName); - for (HashSet<HTMLCollection*>::iterator it = m_collectionsInvalidatedAtDocument.begin(), end = m_collectionsInvalidatedAtDocument.end(); it != end; ++it) - (*it)->invalidateCache(attrName); + Vector<LiveNodeList*, 8> lists; + copyToVector(m_listsInvalidatedAtDocument, lists); + for (auto* list : lists) + list->invalidateCacheForAttribute(attrName); + + Vector<HTMLCollection*, 8> collections; + copyToVector(m_collectionsInvalidatedAtDocument, collections); + for (auto* collection : collections) + collection->invalidateCacheForAttribute(attrName); } void Node::invalidateNodeListAndCollectionCachesInAncestors(const QualifiedName* attrName, Element* attributeOwnerElement) @@ -760,7 +879,7 @@ void Node::invalidateNodeListAndCollectionCachesInAncestors(const QualifiedName* NodeListsNodeData* Node::nodeLists() { - return hasRareData() ? rareData()->nodeLists() : 0; + return hasRareData() ? rareData()->nodeLists() : nullptr; } void Node::clearNodeLists() @@ -768,41 +887,36 @@ void Node::clearNodeLists() rareData()->clearNodeLists(); } -void Node::checkSetPrefix(const AtomicString& prefix, ExceptionCode& ec) +ExceptionOr<void> Node::checkSetPrefix(const AtomicString& prefix) { // Perform error checking as required by spec for setting Node.prefix. Used by // Element::setPrefix() and Attr::setPrefix() - if (!prefix.isEmpty() && !Document::isValidName(prefix)) { - ec = INVALID_CHARACTER_ERR; - return; - } - - if (isReadOnlyNode()) { - ec = NO_MODIFICATION_ALLOWED_ERR; - return; - } + if (!prefix.isEmpty() && !Document::isValidName(prefix)) + return Exception { INVALID_CHARACTER_ERR }; // FIXME: Raise NAMESPACE_ERR if prefix is malformed per the Namespaces in XML specification. - const AtomicString& nodeNamespaceURI = namespaceURI(); - if ((nodeNamespaceURI.isEmpty() && !prefix.isEmpty()) - || (prefix == xmlAtom && nodeNamespaceURI != XMLNames::xmlNamespaceURI)) { - ec = NAMESPACE_ERR; - return; - } + auto& namespaceURI = this->namespaceURI(); + if (namespaceURI.isEmpty() && !prefix.isEmpty()) + return Exception { NAMESPACE_ERR }; + if (prefix == xmlAtom && namespaceURI != XMLNames::xmlNamespaceURI) + return Exception { NAMESPACE_ERR }; + // Attribute-specific checks are in Attr::setPrefix(). + + return { }; } -bool Node::isDescendantOf(const Node* other) const +bool Node::isDescendantOf(const Node& other) const { // Return true if other is an ancestor of this, otherwise false - if (!other || !other->hasChildNodes() || inDocument() != other->inDocument()) + if (!other.hasChildNodes() || isConnected() != other.isConnected()) return false; - if (other->isDocumentNode()) - return &document() == other && !isDocumentNode() && inDocument(); - for (const ContainerNode* n = parentNode(); n; n = n->parentNode()) { - if (n == other) + if (other.isDocumentNode()) + return &document() == &other && !isDocumentNode() && isConnected(); + for (const auto* ancestor = parentNode(); ancestor; ancestor = ancestor->parentNode()) { + if (ancestor == &other) return true; } return false; @@ -812,19 +926,19 @@ bool Node::isDescendantOrShadowDescendantOf(const Node* other) const { if (!other) return false; - if (isDescendantOf(other)) + if (isDescendantOf(*other)) return true; const Node* shadowAncestorNode = deprecatedShadowAncestorNode(); if (!shadowAncestorNode) return false; - return shadowAncestorNode == other || shadowAncestorNode->isDescendantOf(other); + return shadowAncestorNode == other || shadowAncestorNode->isDescendantOf(*other); } bool Node::contains(const Node* node) const { if (!node) return false; - return this == node || node->isDescendantOf(this); + return this == node || node->isDescendantOf(*this); } bool Node::containsIncludingShadowDOM(const Node* node) const @@ -838,7 +952,6 @@ bool Node::containsIncludingShadowDOM(const Node* node) const bool Node::containsIncludingHostElements(const Node* node) const { -#if ENABLE(TEMPLATE_ELEMENT) while (node) { if (node == this) return true; @@ -848,14 +961,11 @@ bool Node::containsIncludingHostElements(const Node* node) const node = node->parentOrShadowHostNode(); } return false; -#else - return containsIncludingShadowDOM(node); -#endif } Node* Node::pseudoAwarePreviousSibling() const { - Element* parentOrHost = isPseudoElement() ? toPseudoElement(this)->hostElement() : parentElement(); + Element* parentOrHost = is<PseudoElement>(*this) ? downcast<PseudoElement>(*this).hostElement() : parentElement(); if (parentOrHost && !previousSibling()) { if (isAfterPseudoElement() && parentOrHost->lastChild()) return parentOrHost->lastChild(); @@ -867,7 +977,7 @@ Node* Node::pseudoAwarePreviousSibling() const Node* Node::pseudoAwareNextSibling() const { - Element* parentOrHost = isPseudoElement() ? toPseudoElement(this)->hostElement() : parentElement(); + Element* parentOrHost = is<PseudoElement>(*this) ? downcast<PseudoElement>(*this).hostElement() : parentElement(); if (parentOrHost && !nextSibling()) { if (isBeforePseudoElement() && parentOrHost->firstChild()) return parentOrHost->firstChild(); @@ -879,14 +989,14 @@ Node* Node::pseudoAwareNextSibling() const Node* Node::pseudoAwareFirstChild() const { - if (isElementNode()) { - const Element* currentElement = toElement(this); - Node* first = currentElement->beforePseudoElement(); + if (is<Element>(*this)) { + const Element& currentElement = downcast<Element>(*this); + Node* first = currentElement.beforePseudoElement(); if (first) return first; - first = currentElement->firstChild(); + first = currentElement.firstChild(); if (!first) - first = currentElement->afterPseudoElement(); + first = currentElement.afterPseudoElement(); return first; } return firstChild(); @@ -894,26 +1004,25 @@ Node* Node::pseudoAwareFirstChild() const Node* Node::pseudoAwareLastChild() const { - if (isElementNode()) { - const Element* currentElement = toElement(this); - Node* last = currentElement->afterPseudoElement(); + if (is<Element>(*this)) { + const Element& currentElement = downcast<Element>(*this); + Node* last = currentElement.afterPseudoElement(); if (last) return last; - last = currentElement->lastChild(); + last = currentElement.lastChild(); if (!last) - last = currentElement->beforePseudoElement(); + last = currentElement.beforePseudoElement(); return last; } return lastChild(); } -RenderStyle* Node::computedStyle(PseudoId pseudoElementSpecifier) +const RenderStyle* Node::computedStyle(PseudoId pseudoElementSpecifier) { - for (Node* node = this; node; node = node->parentOrShadowHostNode()) { - if (node->isElementNode()) - return toElement(node)->computedStyle(pseudoElementSpecifier); - } - return nullptr; + auto* composedParent = composedTreeAncestors(*this).first(); + if (!composedParent) + return nullptr; + return composedParent->computedStyle(pseudoElementSpecifier); } int Node::maxCharacterOffset() const @@ -942,22 +1051,108 @@ bool Node::canStartSelection() const Element* Node::shadowHost() const { if (ShadowRoot* root = containingShadowRoot()) - return root->hostElement(); - return 0; + return root->host(); + return nullptr; } Node* Node::deprecatedShadowAncestorNode() const { if (ShadowRoot* root = containingShadowRoot()) - return root->hostElement(); + return root->host(); return const_cast<Node*>(this); } ShadowRoot* Node::containingShadowRoot() const { - ContainerNode* root = treeScope().rootNode(); - return root && root->isShadowRoot() ? toShadowRoot(root) : 0; + ContainerNode& root = treeScope().rootNode(); + return is<ShadowRoot>(root) ? downcast<ShadowRoot>(&root) : nullptr; +} + +#if !ASSERT_DISABLED +// https://dom.spec.whatwg.org/#concept-closed-shadow-hidden +static bool isClosedShadowHiddenUsingSpecDefinition(const Node& A, const Node& B) +{ + return A.isInShadowTree() + && !A.rootNode().containsIncludingShadowDOM(&B) + && (A.containingShadowRoot()->mode() != ShadowRootMode::Open || isClosedShadowHiddenUsingSpecDefinition(*A.shadowHost(), B)); +} +#endif + +// http://w3c.github.io/webcomponents/spec/shadow/#dfn-unclosed-node +bool Node::isClosedShadowHidden(const Node& otherNode) const +{ + // Use Vector instead of HashSet since we expect the number of ancestor tree scopes to be small. + Vector<TreeScope*, 8> ancestorScopesOfThisNode; + + for (auto* scope = &treeScope(); scope; scope = scope->parentTreeScope()) + ancestorScopesOfThisNode.append(scope); + + for (auto* treeScopeThatCanAccessOtherNode = &otherNode.treeScope(); treeScopeThatCanAccessOtherNode; treeScopeThatCanAccessOtherNode = treeScopeThatCanAccessOtherNode->parentTreeScope()) { + for (auto* scope : ancestorScopesOfThisNode) { + if (scope == treeScopeThatCanAccessOtherNode) { + ASSERT(!isClosedShadowHiddenUsingSpecDefinition(otherNode, *this)); + return false; // treeScopeThatCanAccessOtherNode is a shadow-including inclusive ancestor of this node. + } + } + auto& root = treeScopeThatCanAccessOtherNode->rootNode(); + if (is<ShadowRoot>(root) && downcast<ShadowRoot>(root).mode() != ShadowRootMode::Open) + break; + } + + ASSERT(isClosedShadowHiddenUsingSpecDefinition(otherNode, *this)); + return true; +} + +static inline ShadowRoot* parentShadowRoot(const Node& node) +{ + if (auto* parent = node.parentElement()) + return parent->shadowRoot(); + return nullptr; +} + +HTMLSlotElement* Node::assignedSlot() const +{ + if (auto* shadowRoot = parentShadowRoot(*this)) + return shadowRoot->findAssignedSlot(*this); + return nullptr; +} + +HTMLSlotElement* Node::assignedSlotForBindings() const +{ + auto* shadowRoot = parentShadowRoot(*this); + if (shadowRoot && shadowRoot->mode() == ShadowRootMode::Open) + return shadowRoot->findAssignedSlot(*this); + return nullptr; +} + +ContainerNode* Node::parentInComposedTree() const +{ + ASSERT(isMainThreadOrGCThread()); + if (auto* slot = assignedSlot()) + return slot; + if (is<ShadowRoot>(*this)) + return downcast<ShadowRoot>(*this).host(); + return parentNode(); +} + +Element* Node::parentElementInComposedTree() const +{ + if (auto* slot = assignedSlot()) + return slot; + if (auto* parent = parentNode()) { + if (is<ShadowRoot>(*parent)) + return downcast<ShadowRoot>(*parent).host(); + if (is<Element>(*parent)) + return downcast<Element>(parent); + } + return nullptr; +} + +bool Node::isInUserAgentShadowTree() const +{ + auto* shadowRoot = containingShadowRoot(); + return shadowRoot && shadowRoot->mode() == ShadowRootMode::UserAgent; } Node* Node::nonBoundaryShadowTreeRootNode() @@ -978,45 +1173,70 @@ Node* Node::nonBoundaryShadowTreeRootNode() ContainerNode* Node::nonShadowBoundaryParentNode() const { ContainerNode* parent = parentNode(); - return parent && !parent->isShadowRoot() ? parent : 0; + return parent && !parent->isShadowRoot() ? parent : nullptr; } Element* Node::parentOrShadowHostElement() const { ContainerNode* parent = parentOrShadowHostNode(); if (!parent) - return 0; + return nullptr; + + if (is<ShadowRoot>(*parent)) + return downcast<ShadowRoot>(*parent).host(); + + if (!is<Element>(*parent)) + return nullptr; + + return downcast<Element>(parent); +} - if (parent->isShadowRoot()) - return toShadowRoot(parent)->hostElement(); +Node& Node::rootNode() const +{ + if (isInTreeScope()) + return treeScope().rootNode(); - if (!parent->isElementNode()) - return 0; + Node* node = const_cast<Node*>(this); + Node* highest = node; + for (; node; node = node->parentNode()) + highest = node; + return *highest; +} - return toElement(parent); +// https://dom.spec.whatwg.org/#concept-shadow-including-root +Node& Node::shadowIncludingRoot() const +{ + auto& root = rootNode(); + if (!is<ShadowRoot>(root)) + return root; + auto* host = downcast<ShadowRoot>(root).host(); + return host ? host->shadowIncludingRoot() : root; } -Node* Node::insertionParentForBinding() const +Node& Node::getRootNode(const GetRootNodeOptions& options) const { - return findInsertionPointOf(this); + return options.composed ? shadowIncludingRoot() : rootNode(); } Node::InsertionNotificationRequest Node::insertedInto(ContainerNode& insertionPoint) { - ASSERT(insertionPoint.inDocument() || isContainerNode()); - if (insertionPoint.inDocument()) - setFlag(InDocumentFlag); + ASSERT(insertionPoint.isConnected() || isContainerNode()); + if (insertionPoint.isConnected()) + setFlag(IsConnectedFlag); if (parentOrShadowHostNode()->isInShadowTree()) setFlag(IsInShadowTreeFlag); + + invalidateStyle(Style::Validity::SubtreeAndRenderersInvalid); + return InsertionDone; } void Node::removedFrom(ContainerNode& insertionPoint) { - ASSERT(insertionPoint.inDocument() || isContainerNode()); - if (insertionPoint.inDocument()) - clearFlag(InDocumentFlag); - if (isInShadowTree() && !treeScope().rootNode()->isShadowRoot()) + ASSERT(insertionPoint.isConnected() || isContainerNode()); + if (insertionPoint.isConnected()) + clearFlag(IsConnectedFlag); + if (isInShadowTree() && !treeScope().rootNode().isShadowRoot()) clearFlag(IsInShadowTreeFlag); } @@ -1026,23 +1246,13 @@ bool Node::isRootEditableElement() const || !parentNode()->isElementNode() || hasTagName(bodyTag)); } -Element* Node::rootEditableElement(EditableType editableType) const -{ - if (editableType == HasEditableAXRole) { - if (AXObjectCache* cache = document().existingAXObjectCache()) - return const_cast<Element*>(cache->rootAXEditableElement(this)); - } - - return rootEditableElement(); -} - Element* Node::rootEditableElement() const { - Element* result = 0; - for (Node* n = const_cast<Node*>(this); n && n->hasEditableStyle(); n = n->parentNode()) { - if (n->isElementNode()) - result = toElement(n); - if (n->hasTagName(bodyTag)) + Element* result = nullptr; + for (Node* node = const_cast<Node*>(this); node && node->hasEditableStyle(); node = node->parentNode()) { + if (is<Element>(*node)) + result = downcast<Element>(node); + if (is<HTMLBodyElement>(*node)) break; } return result; @@ -1056,9 +1266,10 @@ Document* Node::ownerDocument() const return document == this ? nullptr : document; } -URL Node::baseURI() const +const URL& Node::baseURI() const { - return parentNode() ? parentNode()->baseURI() : URL(); + auto& url = document().baseURL(); + return url.isNull() ? blankURL() : url; } bool Node::isEqualNode(Node* other) const @@ -1070,23 +1281,58 @@ bool Node::isEqualNode(Node* other) const if (nodeType != other->nodeType()) return false; - if (nodeName() != other->nodeName()) - return false; - - if (localName() != other->localName()) - return false; - - if (namespaceURI() != other->namespaceURI()) - return false; - - if (prefix() != other->prefix()) - return false; - - if (nodeValue() != other->nodeValue()) - return false; - - if (isElementNode() && !toElement(this)->hasEquivalentAttributes(toElement(other))) - return false; + switch (nodeType) { + case Node::DOCUMENT_TYPE_NODE: { + auto& thisDocType = downcast<DocumentType>(*this); + auto& otherDocType = downcast<DocumentType>(*other); + if (thisDocType.name() != otherDocType.name()) + return false; + if (thisDocType.publicId() != otherDocType.publicId()) + return false; + if (thisDocType.systemId() != otherDocType.systemId()) + return false; + break; + } + case Node::ELEMENT_NODE: { + auto& thisElement = downcast<Element>(*this); + auto& otherElement = downcast<Element>(*other); + if (thisElement.tagQName() != otherElement.tagQName()) + return false; + if (!thisElement.hasEquivalentAttributes(&otherElement)) + return false; + break; + } + case Node::PROCESSING_INSTRUCTION_NODE: { + auto& thisProcessingInstruction = downcast<ProcessingInstruction>(*this); + auto& otherProcessingInstruction = downcast<ProcessingInstruction>(*other); + if (thisProcessingInstruction.target() != otherProcessingInstruction.target()) + return false; + if (thisProcessingInstruction.data() != otherProcessingInstruction.data()) + return false; + break; + } + case Node::CDATA_SECTION_NODE: + case Node::TEXT_NODE: + case Node::COMMENT_NODE: { + auto& thisCharacterData = downcast<CharacterData>(*this); + auto& otherCharacterData = downcast<CharacterData>(*other); + if (thisCharacterData.data() != otherCharacterData.data()) + return false; + break; + } + case Node::ATTRIBUTE_NODE: { + auto& thisAttribute = downcast<Attr>(*this); + auto& otherAttribute = downcast<Attr>(*other); + if (thisAttribute.qualifiedName() != otherAttribute.qualifiedName()) + return false; + if (thisAttribute.value() != otherAttribute.value()) + return false; + break; + } + case Node::DOCUMENT_NODE: + case Node::DOCUMENT_FRAGMENT_NODE: + break; + } Node* child = firstChild(); Node* otherChild = other->firstChild(); @@ -1102,184 +1348,106 @@ bool Node::isEqualNode(Node* other) const if (otherChild) return false; - if (nodeType == DOCUMENT_TYPE_NODE) { - const DocumentType* documentTypeThis = static_cast<const DocumentType*>(this); - const DocumentType* documentTypeOther = static_cast<const DocumentType*>(other); - - if (documentTypeThis->publicId() != documentTypeOther->publicId()) - return false; - - if (documentTypeThis->systemId() != documentTypeOther->systemId()) - return false; - - if (documentTypeThis->internalSubset() != documentTypeOther->internalSubset()) - return false; - - // FIXME: We don't compare entities or notations because currently both are always empty. - } - return true; } -bool Node::isDefaultNamespace(const AtomicString& namespaceURIMaybeEmpty) const +// https://dom.spec.whatwg.org/#locate-a-namespace +static const AtomicString& locateDefaultNamespace(const Node& node, const AtomicString& prefix) { - const AtomicString& namespaceURI = namespaceURIMaybeEmpty.isEmpty() ? nullAtom : namespaceURIMaybeEmpty; + switch (node.nodeType()) { + case Node::ELEMENT_NODE: { + auto& element = downcast<Element>(node); + auto& namespaceURI = element.namespaceURI(); + if (!namespaceURI.isNull() && element.prefix() == prefix) + return namespaceURI; - switch (nodeType()) { - case ELEMENT_NODE: { - const Element* elem = toElement(this); - - if (elem->prefix().isNull()) - return elem->namespaceURI() == namespaceURI; + if (element.hasAttributes()) { + for (auto& attribute : element.attributesIterator()) { + if (attribute.namespaceURI() != XMLNSNames::xmlnsNamespaceURI) + continue; - if (elem->hasAttributes()) { - for (const Attribute& attribute : elem->attributesIterator()) { - if (attribute.localName() == xmlnsAtom) - return attribute.value() == namespaceURI; + if ((prefix.isNull() && attribute.prefix().isNull() && attribute.localName() == xmlnsAtom) || (attribute.prefix() == xmlnsAtom && attribute.localName() == prefix)) { + auto& result = attribute.value(); + return result.isEmpty() ? nullAtom : result; } } - - if (Element* ancestor = ancestorElement()) - return ancestor->isDefaultNamespace(namespaceURI); - - return false; } - case DOCUMENT_NODE: - if (Element* de = toDocument(this)->documentElement()) - return de->isDefaultNamespace(namespaceURI); - return false; - case ENTITY_NODE: - case NOTATION_NODE: - case DOCUMENT_TYPE_NODE: - case DOCUMENT_FRAGMENT_NODE: - return false; - case ATTRIBUTE_NODE: { - const Attr* attr = static_cast<const Attr*>(this); - if (attr->ownerElement()) - return attr->ownerElement()->isDefaultNamespace(namespaceURI); - return false; - } - default: - if (Element* ancestor = ancestorElement()) - return ancestor->isDefaultNamespace(namespaceURI); - return false; + auto* parent = node.parentElement(); + return parent ? locateDefaultNamespace(*parent, prefix) : nullAtom; + } + case Node::DOCUMENT_NODE: + if (auto* documentElement = downcast<Document>(node).documentElement()) + return locateDefaultNamespace(*documentElement, prefix); + return nullAtom; + case Node::DOCUMENT_TYPE_NODE: + case Node::DOCUMENT_FRAGMENT_NODE: + return nullAtom; + case Node::ATTRIBUTE_NODE: + if (auto* ownerElement = downcast<Attr>(node).ownerElement()) + return locateDefaultNamespace(*ownerElement, prefix); + return nullAtom; + default: + if (auto* parent = node.parentElement()) + return locateDefaultNamespace(*parent, prefix); + return nullAtom; } } -String Node::lookupPrefix(const AtomicString &namespaceURI) const +// https://dom.spec.whatwg.org/#dom-node-isdefaultnamespace +bool Node::isDefaultNamespace(const AtomicString& potentiallyEmptyNamespace) const { - // Implemented according to - // http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/namespaces-algorithms.html#lookupNamespacePrefixAlgo - - if (namespaceURI.isEmpty()) - return String(); - - switch (nodeType()) { - case ELEMENT_NODE: - return lookupNamespacePrefix(namespaceURI, static_cast<const Element *>(this)); - case DOCUMENT_NODE: - if (Element* de = toDocument(this)->documentElement()) - return de->lookupPrefix(namespaceURI); - return String(); - case ENTITY_NODE: - case NOTATION_NODE: - case DOCUMENT_FRAGMENT_NODE: - case DOCUMENT_TYPE_NODE: - return String(); - case ATTRIBUTE_NODE: { - const Attr *attr = static_cast<const Attr *>(this); - if (attr->ownerElement()) - return attr->ownerElement()->lookupPrefix(namespaceURI); - return String(); - } - default: - if (Element* ancestor = ancestorElement()) - return ancestor->lookupPrefix(namespaceURI); - return String(); - } + const AtomicString& namespaceURI = potentiallyEmptyNamespace.isEmpty() ? nullAtom : potentiallyEmptyNamespace; + return locateDefaultNamespace(*this, nullAtom) == namespaceURI; } -String Node::lookupNamespaceURI(const String &prefix) const +// https://dom.spec.whatwg.org/#dom-node-lookupnamespaceuri +const AtomicString& Node::lookupNamespaceURI(const AtomicString& potentiallyEmptyPrefix) const { - // Implemented according to - // http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/namespaces-algorithms.html#lookupNamespaceURIAlgo - - if (!prefix.isNull() && prefix.isEmpty()) - return String(); - - switch (nodeType()) { - case ELEMENT_NODE: { - const Element *elem = static_cast<const Element *>(this); - - if (!elem->namespaceURI().isNull() && elem->prefix() == prefix) - return elem->namespaceURI(); - - if (elem->hasAttributes()) { - for (const Attribute& attribute : elem->attributesIterator()) { - - if (attribute.prefix() == xmlnsAtom && attribute.localName() == prefix) { - if (!attribute.value().isEmpty()) - return attribute.value(); - - return String(); - } - if (attribute.localName() == xmlnsAtom && prefix.isNull()) { - if (!attribute.value().isEmpty()) - return attribute.value(); - - return String(); - } - } - } - if (Element* ancestor = ancestorElement()) - return ancestor->lookupNamespaceURI(prefix); - return String(); - } - case DOCUMENT_NODE: - if (Element* de = toDocument(this)->documentElement()) - return de->lookupNamespaceURI(prefix); - return String(); - case ENTITY_NODE: - case NOTATION_NODE: - case DOCUMENT_TYPE_NODE: - case DOCUMENT_FRAGMENT_NODE: - return String(); - case ATTRIBUTE_NODE: { - const Attr *attr = static_cast<const Attr *>(this); - - if (attr->ownerElement()) - return attr->ownerElement()->lookupNamespaceURI(prefix); - else - return String(); - } - default: - if (Element* ancestor = ancestorElement()) - return ancestor->lookupNamespaceURI(prefix); - return String(); - } + const AtomicString& prefix = potentiallyEmptyPrefix.isEmpty() ? nullAtom : potentiallyEmptyPrefix; + return locateDefaultNamespace(*this, prefix); } -String Node::lookupNamespacePrefix(const AtomicString &_namespaceURI, const Element *originalElement) const +// https://dom.spec.whatwg.org/#locate-a-namespace-prefix +static const AtomicString& locateNamespacePrefix(const Element& element, const AtomicString& namespaceURI) { - if (_namespaceURI.isNull()) - return String(); - - if (originalElement->lookupNamespaceURI(prefix()) == _namespaceURI) - return prefix(); - - ASSERT(isElementNode()); - const Element* thisElement = toElement(this); - if (thisElement->hasAttributes()) { - for (const Attribute& attribute : thisElement->attributesIterator()) { - if (attribute.prefix() == xmlnsAtom && attribute.value() == _namespaceURI - && originalElement->lookupNamespaceURI(attribute.localName()) == _namespaceURI) + if (element.namespaceURI() == namespaceURI) + return element.prefix(); + + if (element.hasAttributes()) { + for (auto& attribute : element.attributesIterator()) { + if (attribute.prefix() == xmlnsAtom && attribute.value() == namespaceURI) return attribute.localName(); } } + auto* parent = element.parentElement(); + return parent ? locateNamespacePrefix(*parent, namespaceURI) : nullAtom; +} + +// https://dom.spec.whatwg.org/#dom-node-lookupprefix +const AtomicString& Node::lookupPrefix(const AtomicString& namespaceURI) const +{ + if (namespaceURI.isEmpty()) + return nullAtom; - if (Element* ancestor = ancestorElement()) - return ancestor->lookupNamespacePrefix(_namespaceURI, originalElement); - return String(); + switch (nodeType()) { + case ELEMENT_NODE: + return locateNamespacePrefix(downcast<Element>(*this), namespaceURI); + case DOCUMENT_NODE: + if (auto* documentElement = downcast<Document>(*this).documentElement()) + return locateNamespacePrefix(*documentElement, namespaceURI); + return nullAtom; + case DOCUMENT_FRAGMENT_NODE: + case DOCUMENT_TYPE_NODE: + return nullAtom; + case ATTRIBUTE_NODE: + if (auto* ownerElement = downcast<Attr>(*this).ownerElement()) + return locateNamespacePrefix(*ownerElement, namespaceURI); + return nullAtom; + default: + if (auto* parent = parentElement()) + return locateNamespacePrefix(*parent, namespaceURI); + return nullAtom; + } } static void appendTextContent(const Node* node, bool convertBRsToNewlines, bool& isNullString, StringBuilder& content) @@ -1305,8 +1473,6 @@ static void appendTextContent(const Node* node, bool convertBRsToNewlines, bool& } FALLTHROUGH; case Node::ATTRIBUTE_NODE: - case Node::ENTITY_NODE: - case Node::ENTITY_REFERENCE_NODE: case Node::DOCUMENT_FRAGMENT_NODE: isNullString = false; for (Node* child = node->firstChild(); child; child = child->nextSibling()) { @@ -1318,8 +1484,6 @@ static void appendTextContent(const Node* node, bool convertBRsToNewlines, bool& case Node::DOCUMENT_NODE: case Node::DOCUMENT_TYPE_NODE: - case Node::NOTATION_NODE: - case Node::XPATH_NAMESPACE_NODE: break; } } @@ -1332,83 +1496,78 @@ String Node::textContent(bool convertBRsToNewlines) const return isNullString ? String() : content.toString(); } -void Node::setTextContent(const String& text, ExceptionCode& ec) +ExceptionOr<void> Node::setTextContent(const String& text) { switch (nodeType()) { - case TEXT_NODE: - case CDATA_SECTION_NODE: - case COMMENT_NODE: - case PROCESSING_INSTRUCTION_NODE: - setNodeValue(text, ec); - return; - case ELEMENT_NODE: - case ATTRIBUTE_NODE: - case ENTITY_NODE: - case ENTITY_REFERENCE_NODE: - case DOCUMENT_FRAGMENT_NODE: { - Ref<ContainerNode> container(*toContainerNode(this)); - ChildListMutationScope mutation(container.get()); - container->removeChildren(); - if (!text.isEmpty()) - container->appendChild(document().createTextNode(text), ec); - return; - } - case DOCUMENT_NODE: - case DOCUMENT_TYPE_NODE: - case NOTATION_NODE: - case XPATH_NAMESPACE_NODE: - // Do nothing. - return; + case ATTRIBUTE_NODE: + case TEXT_NODE: + case CDATA_SECTION_NODE: + case COMMENT_NODE: + case PROCESSING_INSTRUCTION_NODE: + return setNodeValue(text); + case ELEMENT_NODE: + case DOCUMENT_FRAGMENT_NODE: { + auto& container = downcast<ContainerNode>(*this); + if (text.isEmpty()) + container.replaceAllChildren(nullptr); + else + container.replaceAllChildren(document().createTextNode(text)); + return { }; + } + case DOCUMENT_NODE: + case DOCUMENT_TYPE_NODE: + // Do nothing. + return { }; } ASSERT_NOT_REACHED(); + return { }; } -Element* Node::ancestorElement() const +bool Node::offsetInCharacters() const { - // In theory, there can be EntityReference nodes between elements, but this is currently not supported. - for (ContainerNode* n = parentNode(); n; n = n->parentNode()) { - if (n->isElementNode()) - return toElement(n); - } - return 0; + return false; } -bool Node::offsetInCharacters() const +static SHA1::Digest hashPointer(void* pointer) { - return false; + SHA1 sha1; + sha1.addBytes(reinterpret_cast<const uint8_t*>(&pointer), sizeof(pointer)); + SHA1::Digest digest; + sha1.computeHash(digest); + return digest; } -static inline unsigned short compareDetachedElementsPosition(Node* firstNode, Node* secondNode) +static inline unsigned short compareDetachedElementsPosition(Node& firstNode, Node& secondNode) { // If the 2 nodes are not in the same tree, return the result of adding DOCUMENT_POSITION_DISCONNECTED, // DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC, and either DOCUMENT_POSITION_PRECEDING or // DOCUMENT_POSITION_FOLLOWING, with the constraint that this is to be consistent. Whether to return - // DOCUMENT_POSITION_PRECEDING or DOCUMENT_POSITION_FOLLOWING is implemented here via pointer - // comparison. - // See step 3 in http://www.w3.org/TR/2012/WD-dom-20121206/#dom-node-comparedocumentposition - unsigned short direction = (firstNode > secondNode) ? Node::DOCUMENT_POSITION_PRECEDING : Node::DOCUMENT_POSITION_FOLLOWING; + // DOCUMENT_POSITION_PRECEDING or DOCUMENT_POSITION_FOLLOWING is implemented by comparing cryptographic + // hashes of Node pointers. + // See step 3 in https://dom.spec.whatwg.org/#dom-node-comparedocumentposition + SHA1::Digest firstHash = hashPointer(&firstNode); + SHA1::Digest secondHash = hashPointer(&secondNode); + + unsigned short direction = memcmp(firstHash.data(), secondHash.data(), SHA1::hashSize) > 0 ? Node::DOCUMENT_POSITION_PRECEDING : Node::DOCUMENT_POSITION_FOLLOWING; + return Node::DOCUMENT_POSITION_DISCONNECTED | Node::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | direction; } -unsigned short Node::compareDocumentPosition(Node* otherNode) +unsigned short Node::compareDocumentPosition(Node& otherNode) { - // It is not clear what should be done if |otherNode| is 0. - if (!otherNode) - return DOCUMENT_POSITION_DISCONNECTED; - - if (otherNode == this) + if (&otherNode == this) return DOCUMENT_POSITION_EQUIVALENT; - Attr* attr1 = isAttributeNode() ? toAttr(this) : nullptr; - Attr* attr2 = otherNode->isAttributeNode() ? toAttr(otherNode) : nullptr; + Attr* attr1 = is<Attr>(*this) ? downcast<Attr>(this) : nullptr; + Attr* attr2 = is<Attr>(otherNode) ? &downcast<Attr>(otherNode) : nullptr; Node* start1 = attr1 ? attr1->ownerElement() : this; - Node* start2 = attr2 ? attr2->ownerElement() : otherNode; + Node* start2 = attr2 ? attr2->ownerElement() : &otherNode; // If either of start1 or start2 is null, then we are disconnected, since one of the nodes is // an orphaned attribute node. if (!start1 || !start2) - return compareDetachedElementsPosition(this, otherNode); + return compareDetachedElementsPosition(*this, otherNode); Vector<Node*, 16> chain1; Vector<Node*, 16> chain2; @@ -1439,10 +1598,9 @@ unsigned short Node::compareDocumentPosition(Node* otherNode) // If one node is in the document and the other is not, we must be disconnected. // If the nodes have different owning documents, they must be disconnected. Note that we avoid - // comparing Attr nodes here, since they return false from inDocument() all the time (which seems like a bug). - if (start1->inDocument() != start2->inDocument() || - &start1->treeScope() != &start2->treeScope()) - return compareDetachedElementsPosition(this, otherNode); + // comparing Attr nodes here, since they return false from isConnected() all the time (which seems like a bug). + if (start1->isConnected() != start2->isConnected() || &start1->treeScope() != &start2->treeScope()) + return compareDetachedElementsPosition(*this, otherNode); // We need to find a common ancestor container, and then compare the indices of the two immediate children. Node* current; @@ -1456,7 +1614,7 @@ unsigned short Node::compareDocumentPosition(Node* otherNode) // If the two elements don't have a common root, they're not in the same tree. if (chain1[index1 - 1] != chain2[index2 - 1]) - return compareDetachedElementsPosition(this, otherNode); + return compareDetachedElementsPosition(*this, otherNode); // Walk the two chains backwards and look for the first difference. for (unsigned i = std::min(index1, index2); i; --i) { @@ -1497,8 +1655,7 @@ FloatPoint Node::convertToPage(const FloatPoint& p) const return renderer()->localToAbsolute(p, UseTransforms); // Otherwise go up the tree looking for a renderer - Element *parent = ancestorElement(); - if (parent) + if (auto* parent = parentElement()) return parent->convertToPage(p); // No parent - no conversion needed @@ -1512,22 +1669,21 @@ FloatPoint Node::convertFromPage(const FloatPoint& p) const return renderer()->absoluteToLocal(p, UseTransforms); // Otherwise go up the tree looking for a renderer - Element *parent = ancestorElement(); - if (parent) + if (auto* parent = parentElement()) return parent->convertFromPage(p); // No parent - no conversion needed return p; } -#ifndef NDEBUG +#if ENABLE(TREE_DEBUGGING) static void appendAttributeDesc(const Node* node, StringBuilder& stringBuilder, const QualifiedName& name, const char* attrDesc) { - if (!node->isElementNode()) + if (!is<Element>(*node)) return; - String attr = toElement(node)->getAttribute(name); + const AtomicString& attr = downcast<Element>(*node).getAttribute(name); if (attr.isEmpty()) return; @@ -1548,7 +1704,7 @@ void Node::showNode(const char* prefix) const StringBuilder attrs; appendAttributeDesc(this, attrs, classAttr, " CLASS="); appendAttributeDesc(this, attrs, styleAttr, " STYLE="); - fprintf(stderr, "%s%s\t%p%s\n", prefix, nodeName().utf8().data(), this, attrs.toString().utf8().data()); + fprintf(stderr, "%s%s\t%p (renderer %p) %s%s%s\n", prefix, nodeName().utf8().data(), this, renderer(), attrs.toString().utf8().data(), needsStyleRecalc() ? " (needs style recalc)" : "", childNeedsStyleRecalc() ? " (child needs style recalc)" : ""); } } @@ -1567,9 +1723,9 @@ void Node::showNodePathForThis() const } for (unsigned index = chain.size(); index > 0; --index) { const Node* node = chain[index - 1]; - if (node->isShadowRoot()) { + if (is<ShadowRoot>(*node)) { int count = 0; - for (const ShadowRoot* shadowRoot = toShadowRoot(node); shadowRoot && shadowRoot != node; shadowRoot = shadowRoot->shadowRoot()) + for (const ShadowRoot* shadowRoot = downcast<ShadowRoot>(node); shadowRoot && shadowRoot != node; shadowRoot = shadowRoot->shadowRoot()) ++count; fprintf(stderr, "/#shadow-root[%d]", count); continue; @@ -1579,8 +1735,8 @@ void Node::showNodePathForThis() const case ELEMENT_NODE: { fprintf(stderr, "/%s", node->nodeName().utf8().data()); - const Element* element = toElement(node); - const AtomicString& idattr = element->getIdAttribute(); + const Element& element = downcast<Element>(*node); + const AtomicString& idattr = element.getIdAttribute(); bool hasIdAttr = !idattr.isNull() && !idattr.isEmpty(); if (node->previousSibling() || node->nextSibling()) { int count = 0; @@ -1610,7 +1766,7 @@ void Node::showNodePathForThis() const static void traverseTreeAndMark(const String& baseIndent, const Node* rootNode, const Node* markedNode1, const char* markedLabel1, const Node* markedNode2, const char* markedLabel2) { - for (const Node* node = rootNode; node; node = NodeTraversal::next(node)) { + for (const Node* node = rootNode; node; node = NodeTraversal::next(*node)) { if (node == markedNode1) fprintf(stderr, "%s", markedLabel1); if (node == markedNode2) @@ -1688,26 +1844,23 @@ void Node::showTreeForThisAcrossFrame() const showSubTreeAcrossFrame(rootNode, this, ""); } -#endif +#endif // ENABLE(TREE_DEBUGGING) // -------- void NodeListsNodeData::invalidateCaches(const QualifiedName* attrName) { - for (auto it = m_atomicNameCaches.begin(), end = m_atomicNameCaches.end(); it != end; ++it) - it->value->invalidateCache(attrName); - - for (auto it = m_nameCaches.begin(), end = m_nameCaches.end(); it != end; ++it) - it->value->invalidateCache(attrName); + for (auto& atomicName : m_atomicNameCaches) + atomicName.value->invalidateCacheForAttribute(attrName); - for (auto it = m_cachedCollections.begin(), end = m_cachedCollections.end(); it != end; ++it) - it->value->invalidateCache(attrName); + for (auto& collection : m_cachedCollections) + collection.value->invalidateCacheForAttribute(attrName); if (attrName) return; - for (auto it = m_tagNodeListCacheNS.begin(), end = m_tagNodeListCacheNS.end(); it != end; ++it) - it->value->invalidateCache(); + for (auto& tagCollection : m_tagCollectionNSCache) + tagCollection.value->invalidateCacheForAttribute(nullptr); } void Node::getSubresourceURLs(ListHashSet<URL>& urls) const @@ -1717,15 +1870,15 @@ void Node::getSubresourceURLs(ListHashSet<URL>& urls) const Element* Node::enclosingLinkEventParentOrSelf() { - for (Node* node = this; node; node = node->parentOrShadowHostNode()) { + for (Node* node = this; node; node = node->parentInComposedTree()) { // For imagemaps, the enclosing link element is the associated area element not the image itself. // So we don't let images be the enclosing link element, even though isLink sometimes returns // true for them. - if (node->isLink() && !isHTMLImageElement(node)) - return toElement(node); + if (node->isLink() && !is<HTMLImageElement>(*node)) + return downcast<Element>(node); } - return 0; + return nullptr; } EventTargetInterface Node::eventTargetInterface() const @@ -1733,69 +1886,58 @@ EventTargetInterface Node::eventTargetInterface() const return NodeEventTargetInterfaceType; } -void Node::didMoveToNewDocument(Document* oldDocument) +void Node::didMoveToNewDocument(Document& oldDocument) { TreeScopeAdopter::ensureDidMoveToNewDocumentWasCalled(oldDocument); - if (const EventTargetData* eventTargetData = this->eventTargetData()) { - const EventListenerMap& listenerMap = eventTargetData->eventListenerMap; - if (!listenerMap.isEmpty()) { - Vector<AtomicString> types = listenerMap.eventTypes(); - for (unsigned i = 0; i < types.size(); ++i) - document().addListenerTypeIfNeeded(types[i]); + if (auto* eventTargetData = this->eventTargetData()) { + if (!eventTargetData->eventListenerMap.isEmpty()) { + for (auto& type : eventTargetData->eventListenerMap.eventTypes()) + document().addListenerTypeIfNeeded(type); } } - if (AXObjectCache::accessibilityEnabled() && oldDocument) - if (AXObjectCache* cache = oldDocument->existingAXObjectCache()) + if (AXObjectCache::accessibilityEnabled()) { + if (auto* cache = oldDocument.existingAXObjectCache()) cache->remove(this); - - const EventListenerVector& mousewheelListeners = getEventListeners(eventNames().mousewheelEvent); - for (size_t i = 0; i < mousewheelListeners.size(); ++i) { - oldDocument->didRemoveWheelEventHandler(); - document().didAddWheelEventHandler(); } - const EventListenerVector& wheelListeners = getEventListeners(eventNames().wheelEvent); - for (size_t i = 0; i < wheelListeners.size(); ++i) { - oldDocument->didRemoveWheelEventHandler(); - document().didAddWheelEventHandler(); + unsigned numWheelEventHandlers = eventListeners(eventNames().mousewheelEvent).size() + eventListeners(eventNames().wheelEvent).size(); + for (unsigned i = 0; i < numWheelEventHandlers; ++i) { + oldDocument.didRemoveWheelEventHandler(*this); + document().didAddWheelEventHandler(*this); } - Vector<AtomicString> touchEventNames = eventNames().touchEventNames(); - for (size_t i = 0; i < touchEventNames.size(); ++i) { - const EventListenerVector& listeners = getEventListeners(touchEventNames[i]); - for (size_t j = 0; j < listeners.size(); ++j) { - oldDocument->didRemoveTouchEventHandler(this); - document().didAddTouchEventHandler(this); - } + unsigned numTouchEventHandlers = 0; + for (auto& name : eventNames().touchEventNames()) + numTouchEventHandlers += eventListeners(name).size(); + + for (unsigned i = 0; i < numTouchEventHandlers; ++i) { + oldDocument.didRemoveTouchEventHandler(*this); + document().didAddTouchEventHandler(*this); } - if (Vector<OwnPtr<MutationObserverRegistration>>* registry = mutationObserverRegistry()) { - for (size_t i = 0; i < registry->size(); ++i) { - document().addMutationObserverTypes(registry->at(i)->mutationTypes()); - } + if (auto* registry = mutationObserverRegistry()) { + for (auto& registration : *registry) + document().addMutationObserverTypes(registration->mutationTypes()); } - if (HashSet<MutationObserverRegistration*>* transientRegistry = transientMutationObserverRegistry()) { - for (HashSet<MutationObserverRegistration*>::iterator iter = transientRegistry->begin(); iter != transientRegistry->end(); ++iter) { - document().addMutationObserverTypes((*iter)->mutationTypes()); - } + if (auto* transientRegistry = transientMutationObserverRegistry()) { + for (auto& registration : *transientRegistry) + document().addMutationObserverTypes(registration->mutationTypes()); } } -static inline bool tryAddEventListener(Node* targetNode, const AtomicString& eventType, PassRefPtr<EventListener> prpListener, bool useCapture) +static inline bool tryAddEventListener(Node* targetNode, const AtomicString& eventType, Ref<EventListener>&& listener, const EventTarget::AddEventListenerOptions& options) { - RefPtr<EventListener> listener = prpListener; - - if (!targetNode->EventTarget::addEventListener(eventType, listener, useCapture)) + if (!targetNode->EventTarget::addEventListener(eventType, listener.copyRef(), options)) return false; targetNode->document().addListenerTypeIfNeeded(eventType); - if (eventType == eventNames().wheelEvent || eventType == eventNames().mousewheelEvent) - targetNode->document().didAddWheelEventHandler(); + if (eventNames().isWheelEventType(eventType)) + targetNode->document().didAddWheelEventHandler(*targetNode); else if (eventNames().isTouchEventType(eventType)) - targetNode->document().didAddTouchEventHandler(targetNode); + targetNode->document().didAddTouchEventHandler(*targetNode); #if PLATFORM(IOS) if (targetNode == &targetNode->document() && eventType == eventNames().scrollEvent) @@ -1806,7 +1948,7 @@ static inline bool tryAddEventListener(Node* targetNode, const AtomicString& eve // This code was added to address <rdar://problem/5846492> Onorientationchange event not working for document.body. // Forward this call to addEventListener() to the window since these are window-only events. if (eventType == eventNames().orientationchangeEvent || eventType == eventNames().resizeEvent) - targetNode->document().domWindow()->addEventListener(eventType, listener, useCapture); + targetNode->document().domWindow()->addEventListener(eventType, WTFMove(listener), options); #if ENABLE(TOUCH_EVENTS) if (eventNames().isTouchEventType(eventType)) @@ -1816,28 +1958,28 @@ static inline bool tryAddEventListener(Node* targetNode, const AtomicString& eve #if ENABLE(IOS_GESTURE_EVENTS) && ENABLE(TOUCH_EVENTS) if (eventType == eventNames().gesturestartEvent || eventType == eventNames().gesturechangeEvent || eventType == eventNames().gestureendEvent) - targetNode->document().addTouchEventListener(targetNode); + targetNode->document().addTouchEventHandler(targetNode); #endif return true; } -bool Node::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture) +bool Node::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options) { - return tryAddEventListener(this, eventType, listener, useCapture); + return tryAddEventListener(this, eventType, WTFMove(listener), options); } -static inline bool tryRemoveEventListener(Node* targetNode, const AtomicString& eventType, EventListener* listener, bool useCapture) +static inline bool tryRemoveEventListener(Node* targetNode, const AtomicString& eventType, EventListener& listener, const EventTarget::ListenerOptions& options) { - if (!targetNode->EventTarget::removeEventListener(eventType, listener, useCapture)) + if (!targetNode->EventTarget::removeEventListener(eventType, listener, options)) return false; // FIXME: Notify Document that the listener has vanished. We need to keep track of a number of // listeners for each type, not just a bool - see https://bugs.webkit.org/show_bug.cgi?id=33861 - if (eventType == eventNames().wheelEvent || eventType == eventNames().mousewheelEvent) - targetNode->document().didRemoveWheelEventHandler(); + if (eventNames().isWheelEventType(eventType)) + targetNode->document().didRemoveWheelEventHandler(*targetNode); else if (eventNames().isTouchEventType(eventType)) - targetNode->document().didRemoveTouchEventHandler(targetNode); + targetNode->document().didRemoveTouchEventHandler(*targetNode); #if PLATFORM(IOS) if (targetNode == &targetNode->document() && eventType == eventNames().scrollEvent) @@ -1847,7 +1989,7 @@ static inline bool tryRemoveEventListener(Node* targetNode, const AtomicString& // This code was added to address <rdar://problem/5846492> Onorientationchange event not working for document.body. // Forward this call to removeEventListener() to the window since these are window-only events. if (eventType == eventNames().orientationchangeEvent || eventType == eventNames().resizeEvent) - targetNode->document().domWindow()->removeEventListener(eventType, listener, useCapture); + targetNode->document().domWindow()->removeEventListener(eventType, listener, options); #if ENABLE(TOUCH_EVENTS) if (eventNames().isTouchEventType(eventType)) @@ -1857,140 +1999,149 @@ static inline bool tryRemoveEventListener(Node* targetNode, const AtomicString& #if ENABLE(IOS_GESTURE_EVENTS) && ENABLE(TOUCH_EVENTS) if (eventType == eventNames().gesturestartEvent || eventType == eventNames().gesturechangeEvent || eventType == eventNames().gestureendEvent) - targetNode->document().removeTouchEventListener(targetNode); + targetNode->document().removeTouchEventHandler(targetNode); #endif return true; } -bool Node::removeEventListener(const AtomicString& eventType, EventListener* listener, bool useCapture) +bool Node::removeEventListener(const AtomicString& eventType, EventListener& listener, const ListenerOptions& options) { - return tryRemoveEventListener(this, eventType, listener, useCapture); + return tryRemoveEventListener(this, eventType, listener, options); } -typedef HashMap<Node*, OwnPtr<EventTargetData>> EventTargetDataMap; +typedef HashMap<Node*, std::unique_ptr<EventTargetData>> EventTargetDataMap; static EventTargetDataMap& eventTargetDataMap() { - DEFINE_STATIC_LOCAL(EventTargetDataMap, map, ()); + static NeverDestroyed<EventTargetDataMap> map; + return map; } +static StaticLock s_eventTargetDataMapLock; + EventTargetData* Node::eventTargetData() { - return hasEventTargetData() ? eventTargetDataMap().get(this) : 0; + return hasEventTargetData() ? eventTargetDataMap().get(this) : nullptr; +} + +EventTargetData* Node::eventTargetDataConcurrently() +{ + auto locker = holdLock(s_eventTargetDataMapLock); + return hasEventTargetData() ? eventTargetDataMap().get(this) : nullptr; } EventTargetData& Node::ensureEventTargetData() { if (hasEventTargetData()) return *eventTargetDataMap().get(this); + + auto locker = holdLock(s_eventTargetDataMapLock); setHasEventTargetData(true); - EventTargetData* data = new EventTargetData; - eventTargetDataMap().set(this, adoptPtr(data)); - return *data; + return *eventTargetDataMap().add(this, std::make_unique<EventTargetData>()).iterator->value; } void Node::clearEventTargetData() { + auto locker = holdLock(s_eventTargetDataMapLock); eventTargetDataMap().remove(this); } -Vector<OwnPtr<MutationObserverRegistration>>* Node::mutationObserverRegistry() +Vector<std::unique_ptr<MutationObserverRegistration>>* Node::mutationObserverRegistry() { if (!hasRareData()) - return 0; - NodeMutationObserverData* data = rareData()->mutationObserverData(); + return nullptr; + auto* data = rareData()->mutationObserverData(); if (!data) - return 0; + return nullptr; return &data->registry; } HashSet<MutationObserverRegistration*>* Node::transientMutationObserverRegistry() { if (!hasRareData()) - return 0; - NodeMutationObserverData* data = rareData()->mutationObserverData(); + return nullptr; + auto* data = rareData()->mutationObserverData(); if (!data) - return 0; + return nullptr; return &data->transientRegistry; } -template<typename Registry> -static inline void collectMatchingObserversForMutation(HashMap<MutationObserver*, MutationRecordDeliveryOptions>& observers, Registry* registry, Node* target, MutationObserver::MutationType type, const QualifiedName* attributeName) +template<typename Registry> static inline void collectMatchingObserversForMutation(HashMap<MutationObserver*, MutationRecordDeliveryOptions>& observers, Registry* registry, Node& target, MutationObserver::MutationType type, const QualifiedName* attributeName) { if (!registry) return; - for (typename Registry::iterator iter = registry->begin(); iter != registry->end(); ++iter) { - const MutationObserverRegistration& registration = **iter; - if (registration.shouldReceiveMutationFrom(target, type, attributeName)) { - MutationRecordDeliveryOptions deliveryOptions = registration.deliveryOptions(); - HashMap<MutationObserver*, MutationRecordDeliveryOptions>::AddResult result = observers.add(registration.observer(), deliveryOptions); + + for (auto& registration : *registry) { + if (registration->shouldReceiveMutationFrom(target, type, attributeName)) { + auto deliveryOptions = registration->deliveryOptions(); + auto result = observers.add(®istration->observer(), deliveryOptions); if (!result.isNewEntry) result.iterator->value |= deliveryOptions; } } } -void Node::getRegisteredMutationObserversOfType(HashMap<MutationObserver*, MutationRecordDeliveryOptions>& observers, MutationObserver::MutationType type, const QualifiedName* attributeName) +HashMap<MutationObserver*, MutationRecordDeliveryOptions> Node::registeredMutationObservers(MutationObserver::MutationType type, const QualifiedName* attributeName) { + HashMap<MutationObserver*, MutationRecordDeliveryOptions> result; ASSERT((type == MutationObserver::Attributes && attributeName) || !attributeName); - collectMatchingObserversForMutation(observers, mutationObserverRegistry(), this, type, attributeName); - collectMatchingObserversForMutation(observers, transientMutationObserverRegistry(), this, type, attributeName); + collectMatchingObserversForMutation(result, mutationObserverRegistry(), *this, type, attributeName); + collectMatchingObserversForMutation(result, transientMutationObserverRegistry(), *this, type, attributeName); for (Node* node = parentNode(); node; node = node->parentNode()) { - collectMatchingObserversForMutation(observers, node->mutationObserverRegistry(), this, type, attributeName); - collectMatchingObserversForMutation(observers, node->transientMutationObserverRegistry(), this, type, attributeName); + collectMatchingObserversForMutation(result, node->mutationObserverRegistry(), *this, type, attributeName); + collectMatchingObserversForMutation(result, node->transientMutationObserverRegistry(), *this, type, attributeName); } + return result; } -void Node::registerMutationObserver(MutationObserver* observer, MutationObserverOptions options, const HashSet<AtomicString>& attributeFilter) +void Node::registerMutationObserver(MutationObserver& observer, MutationObserverOptions options, const HashSet<AtomicString>& attributeFilter) { - MutationObserverRegistration* registration = 0; - Vector<OwnPtr<MutationObserverRegistration>>& registry = ensureRareData().ensureMutationObserverData().registry; - for (size_t i = 0; i < registry.size(); ++i) { - if (registry[i]->observer() == observer) { - registration = registry[i].get(); + MutationObserverRegistration* registration = nullptr; + auto& registry = ensureRareData().ensureMutationObserverData().registry; + + for (auto& candidateRegistration : registry) { + if (&candidateRegistration->observer() == &observer) { + registration = candidateRegistration.get(); registration->resetObservation(options, attributeFilter); } } if (!registration) { - registry.append(MutationObserverRegistration::create(observer, this, options, attributeFilter)); + registry.append(std::make_unique<MutationObserverRegistration>(observer, *this, options, attributeFilter)); registration = registry.last().get(); } document().addMutationObserverTypes(registration->mutationTypes()); } -void Node::unregisterMutationObserver(MutationObserverRegistration* registration) +void Node::unregisterMutationObserver(MutationObserverRegistration& registration) { - Vector<OwnPtr<MutationObserverRegistration>>* registry = mutationObserverRegistry(); + auto* registry = mutationObserverRegistry(); ASSERT(registry); if (!registry) return; - size_t index = registry->find(registration); - ASSERT(index != notFound); - if (index == notFound) - return; - - registry->remove(index); + registry->removeFirstMatching([®istration] (auto& current) { + return current.get() == ®istration; + }); } -void Node::registerTransientMutationObserver(MutationObserverRegistration* registration) +void Node::registerTransientMutationObserver(MutationObserverRegistration& registration) { - ensureRareData().ensureMutationObserverData().transientRegistry.add(registration); + ensureRareData().ensureMutationObserverData().transientRegistry.add(®istration); } -void Node::unregisterTransientMutationObserver(MutationObserverRegistration* registration) +void Node::unregisterTransientMutationObserver(MutationObserverRegistration& registration) { - HashSet<MutationObserverRegistration*>* transientRegistry = transientMutationObserverRegistry(); + auto* transientRegistry = transientMutationObserverRegistry(); ASSERT(transientRegistry); if (!transientRegistry) return; - ASSERT(transientRegistry->contains(registration)); - transientRegistry->remove(registration); + ASSERT(transientRegistry->contains(®istration)); + transientRegistry->remove(®istration); } void Node::notifyMutationObserversNodeWillDetach() @@ -1999,15 +2150,13 @@ void Node::notifyMutationObserversNodeWillDetach() return; for (Node* node = parentNode(); node; node = node->parentNode()) { - if (Vector<OwnPtr<MutationObserverRegistration>>* registry = node->mutationObserverRegistry()) { - const size_t size = registry->size(); - for (size_t i = 0; i < size; ++i) - registry->at(i)->observedSubtreeNodeWillDetach(this); + if (auto* registry = node->mutationObserverRegistry()) { + for (auto& registration : *registry) + registration->observedSubtreeNodeWillDetach(*this); } - - if (HashSet<MutationObserverRegistration*>* transientRegistry = node->transientMutationObserverRegistry()) { - for (HashSet<MutationObserverRegistration*>::iterator iter = transientRegistry->begin(); iter != transientRegistry->end(); ++iter) - (*iter)->observedSubtreeNodeWillDetach(this); + if (auto* transientRegistry = node->transientMutationObserverRegistry()) { + for (auto* registration : *transientRegistry) + registration->observedSubtreeNodeWillDetach(*this); } } } @@ -2017,24 +2166,25 @@ void Node::handleLocalEvents(Event& event) if (!hasEventTargetData()) return; - if (isElementNode() && toElement(*this).isDisabledFormControl() && event.isMouseEvent()) + // FIXME: Should we deliver wheel events to disabled form controls or not? + if (is<Element>(*this) && downcast<Element>(*this).isDisabledFormControl() && event.isMouseEvent() && !event.isWheelEvent()) return; - fireEventListeners(&event); + fireEventListeners(event); } -void Node::dispatchScopedEvent(PassRefPtr<Event> event) +void Node::dispatchScopedEvent(Event& event) { EventDispatcher::dispatchScopedEvent(*this, event); } -bool Node::dispatchEvent(PassRefPtr<Event> event) +bool Node::dispatchEvent(Event& event) { #if ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS) - if (event->isTouchEvent()) - return dispatchTouchEvent(adoptRef(toTouchEvent(event.leakRef()))); + if (is<TouchEvent>(event)) + return dispatchTouchEvent(downcast<TouchEvent>(event)); #endif - return EventDispatcher::dispatchEvent(this, event); + return EventDispatcher::dispatchEvent(*this, event); } void Node::dispatchSubtreeModifiedEvent() @@ -2042,7 +2192,7 @@ void Node::dispatchSubtreeModifiedEvent() if (isInShadowTree()) return; - ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden()); + ASSERT_WITH_SECURITY_IMPLICATION(NoEventDispatchAssertion::isEventDispatchAllowedInSubtree(*this)); if (!document().hasListenerType(Document::DOMSUBTREEMODIFIED_LISTENER)) return; @@ -2053,38 +2203,30 @@ void Node::dispatchSubtreeModifiedEvent() dispatchScopedEvent(MutationEvent::create(subtreeModifiedEventName, true)); } -bool Node::dispatchDOMActivateEvent(int detail, PassRefPtr<Event> underlyingEvent) +bool Node::dispatchDOMActivateEvent(int detail, Event& underlyingEvent) { - ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden()); - RefPtr<UIEvent> event = UIEvent::create(eventNames().DOMActivateEvent, true, true, document().defaultView(), detail); - event->setUnderlyingEvent(underlyingEvent); + ASSERT_WITH_SECURITY_IMPLICATION(NoEventDispatchAssertion::isEventAllowedInMainThread()); + Ref<UIEvent> event = UIEvent::create(eventNames().DOMActivateEvent, true, true, document().defaultView(), detail); + event->setUnderlyingEvent(&underlyingEvent); dispatchScopedEvent(event); return event->defaultHandled(); } #if ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS) -bool Node::dispatchTouchEvent(PassRefPtr<TouchEvent> event) +bool Node::dispatchTouchEvent(TouchEvent& event) { - return EventDispatcher::dispatchEvent(this, event); + return EventDispatcher::dispatchEvent(*this, event); } #endif -#if ENABLE(INDIE_UI) -bool Node::dispatchUIRequestEvent(PassRefPtr<UIRequestEvent> event) -{ - EventDispatcher::dispatchEvent(this, event); - return event->defaultHandled() || event->defaultPrevented(); -} -#endif - bool Node::dispatchBeforeLoadEvent(const String& sourceURL) { if (!document().hasListenerType(Document::BEFORELOAD_LISTENER)) return true; - Ref<Node> protect(*this); - RefPtr<BeforeLoadEvent> beforeLoadEvent = BeforeLoadEvent::create(sourceURL); - dispatchEvent(beforeLoadEvent.get()); + Ref<Node> protectedThis(*this); + Ref<BeforeLoadEvent> beforeLoadEvent = BeforeLoadEvent::create(sourceURL); + dispatchEvent(beforeLoadEvent); return !beforeLoadEvent->defaultPrevented(); } @@ -2093,19 +2235,20 @@ void Node::dispatchInputEvent() dispatchScopedEvent(Event::create(eventNames().inputEvent, true, false)); } -void Node::defaultEventHandler(Event* event) +void Node::defaultEventHandler(Event& event) { - if (event->target() != this) + if (event.target() != this) return; - const AtomicString& eventType = event->type(); + const AtomicString& eventType = event.type(); if (eventType == eventNames().keydownEvent || eventType == eventNames().keypressEvent) { - if (event->isKeyboardEvent()) + if (is<KeyboardEvent>(event)) { if (Frame* frame = document().frame()) - frame->eventHandler().defaultKeyboardEventHandler(static_cast<KeyboardEvent*>(event)); + frame->eventHandler().defaultKeyboardEventHandler(downcast<KeyboardEvent>(event)); + } } else if (eventType == eventNames().clickEvent) { - int detail = event->isUIEvent() ? static_cast<UIEvent*>(event)->detail() : 0; + int detail = is<UIEvent>(event) ? downcast<UIEvent>(event).detail() : 0; if (dispatchDOMActivateEvent(detail, event)) - event->setDefaultHandled(); + event.setDefaultHandled(); #if ENABLE(CONTEXT_MENUS) } else if (eventType == eventNames().contextmenuEvent) { if (Frame* frame = document().frame()) @@ -2113,29 +2256,27 @@ void Node::defaultEventHandler(Event* event) page->contextMenuController().handleContextMenuEvent(event); #endif } else if (eventType == eventNames().textInputEvent) { - if (event->eventInterface() == TextEventInterfaceType) + if (is<TextEvent>(event)) { if (Frame* frame = document().frame()) - frame->eventHandler().defaultTextInputEventHandler(static_cast<TextEvent*>(event)); + frame->eventHandler().defaultTextInputEventHandler(downcast<TextEvent>(event)); + } #if ENABLE(PAN_SCROLLING) - } else if (eventType == eventNames().mousedownEvent && event->isMouseEvent()) { - MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); - if (mouseEvent->button() == MiddleButton) { + } else if (eventType == eventNames().mousedownEvent && is<MouseEvent>(event)) { + if (downcast<MouseEvent>(event).button() == MiddleButton) { if (enclosingLinkEventParentOrSelf()) return; RenderObject* renderer = this->renderer(); - while (renderer && (!renderer->isBox() || !toRenderBox(renderer)->canBeScrolledAndHasScrollableArea())) + while (renderer && (!is<RenderBox>(*renderer) || !downcast<RenderBox>(*renderer).canBeScrolledAndHasScrollableArea())) renderer = renderer->parent(); if (renderer) { if (Frame* frame = document().frame()) - frame->eventHandler().startPanScrolling(toRenderBox(renderer)); + frame->eventHandler().startPanScrolling(downcast<RenderBox>(*renderer)); } } #endif - } else if ((eventType == eventNames().wheelEvent || eventType == eventNames().mousewheelEvent) && event->eventInterface() == WheelEventInterfaceType) { - WheelEvent* wheelEvent = static_cast<WheelEvent*>(event); - + } else if (eventNames().isWheelEventType(eventType) && is<WheelEvent>(event)) { // If we don't have a renderer, send the wheel event to the first node we find with a renderer. // This is needed for <option> and <optgroup> elements so that <select>s get a wheel scroll. Node* startNode = this; @@ -2144,22 +2285,18 @@ void Node::defaultEventHandler(Event* event) if (startNode && startNode->renderer()) if (Frame* frame = document().frame()) - frame->eventHandler().defaultWheelEventHandler(startNode, wheelEvent); + frame->eventHandler().defaultWheelEventHandler(startNode, downcast<WheelEvent>(event)); #if ENABLE(TOUCH_EVENTS) && PLATFORM(IOS) - } else if (event->eventInterface() == TouchEventInterfaceType && eventNames().isTouchEventType(eventType)) { - TouchEvent* touchEvent = static_cast<TouchEvent*>(event); - + } else if (is<TouchEvent>(event) && eventNames().isTouchEventType(eventType)) { RenderObject* renderer = this->renderer(); - while (renderer && (!renderer->isBox() || !toRenderBox(renderer)->canBeScrolledAndHasScrollableArea())) + while (renderer && (!is<RenderBox>(*renderer) || !downcast<RenderBox>(*renderer).canBeScrolledAndHasScrollableArea())) renderer = renderer->parent(); if (renderer && renderer->node()) { if (Frame* frame = document().frame()) - frame->eventHandler().defaultTouchEventHandler(renderer->node(), touchEvent); + frame->eventHandler().defaultTouchEventHandler(*renderer->node(), downcast<TouchEvent>(event)); } #endif - } else if (event->type() == eventNames().webkitEditableContentChangedEvent) { - dispatchInputEvent(); } } @@ -2167,9 +2304,9 @@ bool Node::willRespondToMouseMoveEvents() { // FIXME: Why is the iOS code path different from the non-iOS code path? #if !PLATFORM(IOS) - if (!isElementNode()) + if (!is<Element>(*this)) return false; - if (toElement(this)->isDisabledFormControl()) + if (downcast<Element>(*this).isDisabledFormControl()) return false; #endif return hasEventListeners(eventNames().mousemoveEvent) || hasEventListeners(eventNames().mouseoverEvent) || hasEventListeners(eventNames().mouseoutEvent); @@ -2181,11 +2318,12 @@ bool Node::willRespondToMouseClickEvents() #if PLATFORM(IOS) return isContentEditable() || hasEventListeners(eventNames().mouseupEvent) || hasEventListeners(eventNames().mousedownEvent) || hasEventListeners(eventNames().clickEvent); #else - if (!isElementNode()) + if (!is<Element>(*this)) return false; - if (toElement(this)->isDisabledFormControl()) + if (downcast<Element>(*this).isDisabledFormControl()) return false; - return isContentEditable(UserSelectAllIsAlwaysNonEditable) || hasEventListeners(eventNames().mouseupEvent) || hasEventListeners(eventNames().mousedownEvent) || hasEventListeners(eventNames().clickEvent) || hasEventListeners(eventNames().DOMActivateEvent); + return computeEditability(UserSelectAllIsAlwaysNonEditable, ShouldUpdateStyle::Update) != Editability::ReadOnly + || hasEventListeners(eventNames().mouseupEvent) || hasEventListeners(eventNames().mousedownEvent) || hasEventListeners(eventNames().clickEvent) || hasEventListeners(eventNames().DOMActivateEvent); #endif } @@ -2194,31 +2332,6 @@ bool Node::willRespondToMouseWheelEvents() return hasEventListeners(eventNames().mousewheelEvent); } -// This is here so it can be inlined into Node::removedLastRef. -// FIXME: Really? Seems like this could be inlined into Node::removedLastRef if it was in TreeScope.h. -// FIXME: It also not seem important to inline this. Is this really hot? -inline void TreeScope::removedLastRefToScope() -{ - ASSERT(!deletionHasBegun()); - if (m_selfOnlyRefCount) { - // If removing a child removes the last self-only ref, we don't want the scope to be destroyed - // until after removeDetachedChildren returns, so we protect ourselves with an extra self-only ref. - selfOnlyRef(); - dropChildren(); -#ifndef NDEBUG - // We need to do this right now since selfOnlyDeref() can delete this. - rootNode()->m_inRemovedLastRefFunction = false; -#endif - selfOnlyDeref(); - } else { -#ifndef NDEBUG - rootNode()->m_inRemovedLastRefFunction = false; - beginDeletion(); -#endif - delete this; - } -} - // It's important not to inline removedLastRef, because we don't want to inline the code to // delete a Node at each deref call site. void Node::removedLastRef() @@ -2226,8 +2339,8 @@ void Node::removedLastRef() // An explicit check for Document here is better than a virtual function since it is // faster for non-Document nodes, and because the call to removedLastRef that is inlined // at all deref call sites is smaller if it's a non-virtual function. - if (isTreeScope()) { - treeScope().removedLastRefToScope(); + if (is<Document>(*this)) { + downcast<Document>(*this).removedLastRef(); return; } @@ -2239,9 +2352,9 @@ void Node::removedLastRef() void Node::textRects(Vector<IntRect>& rects) const { - RefPtr<Range> range = Range::create(document()); - range->selectNodeContents(const_cast<Node*>(this), IGNORE_EXCEPTION); - range->textRects(rects); + auto range = Range::create(document()); + range->selectNodeContents(const_cast<Node&>(*this)); + range->absoluteTextRects(rects); } unsigned Node::connectedSubframeCount() const @@ -2284,12 +2397,24 @@ void Node::updateAncestorConnectedSubframeCountForInsertion() const bool Node::inRenderedDocument() const { - return inDocument() && document().hasLivingRenderTree(); + return isConnected() && document().hasLivingRenderTree(); +} + +void* Node::opaqueRootSlow() const +{ + const Node* node = this; + for (;;) { + const Node* nextNode = node->parentOrShadowHostNode(); + if (!nextNode) + break; + node = nextNode; + } + return const_cast<void*>(static_cast<const void*>(node)); } } // namespace WebCore -#ifndef NDEBUG +#if ENABLE(TREE_DEBUGGING) void showTree(const WebCore::Node* node) { @@ -2303,4 +2428,4 @@ void showNodePath(const WebCore::Node* node) node->showNodePathForThis(); } -#endif +#endif // ENABLE(TREE_DEBUGGING) diff --git a/Source/WebCore/dom/Node.h b/Source/WebCore/dom/Node.h index dff49ff96..37a7f5dff 100644 --- a/Source/WebCore/dom/Node.h +++ b/Source/WebCore/dom/Node.h @@ -22,84 +22,47 @@ * */ -#ifndef Node_h -#define Node_h +#pragma once -#include "EditingBoundary.h" #include "EventTarget.h" -#include "URLHash.h" +#include "ExceptionOr.h" #include "LayoutRect.h" #include "MutationObserver.h" #include "RenderStyleConstants.h" -#include "ScriptWrappable.h" -#include "SimulatedClickOptions.h" +#include "StyleValidity.h" #include "TreeScope.h" -#include "TreeShared.h" +#include "URLHash.h" #include <wtf/Forward.h> #include <wtf/ListHashSet.h> -#include <wtf/text/AtomicString.h> - -namespace JSC { - class VM; - class SlotVisitor; -} +#include <wtf/MainThread.h> +#include <wtf/TypeCasts.h> // This needs to be here because Document.h also depends on it. #define DUMP_NODE_STATISTICS 0 namespace WebCore { -class Attribute; -class ClassNodeList; class ContainerNode; -class DOMSettableTokenList; class Document; class Element; -class Event; -class EventListener; class FloatPoint; -class Frame; -class HTMLInputElement; -class IntRect; -class KeyboardEvent; -class NSResolver; +class HTMLQualifiedName; +class HTMLSlotElement; +class MathMLQualifiedName; class NamedNodeMap; -class NameNodeList; class NodeList; class NodeListsNodeData; class NodeRareData; class QualifiedName; -class RadioNodeList; -class RegisteredEventListener; class RenderBox; class RenderBoxModelObject; class RenderObject; class RenderStyle; +class SVGQualifiedName; class ShadowRoot; -class TagNodeList; - -#if ENABLE(INDIE_UI) -class UIRequestEvent; -#endif - -#if ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS) class TouchEvent; -#endif - -typedef int ExceptionCode; -const int nodeStyleChangeShift = 14; - -// SyntheticStyleChange means that we need to go through the entire style change logic even though -// no style property has actually changed. It is used to restructure the tree when, for instance, -// RenderLayers are created or destroyed due to animation changes. -enum StyleChangeType { - NoStyleChange = 0, - InlineStyleChange = 1 << nodeStyleChangeShift, - FullStyleChange = 2 << nodeStyleChangeShift, - SyntheticStyleChange = 3 << nodeStyleChangeShift, - ReconstructRenderTree = 4 << nodeStyleChangeShift, -}; +using NodeOrString = Variant<RefPtr<Node>, String>; class NodeRareDataBase { public: @@ -115,26 +78,28 @@ private: RenderObject* m_renderer; }; -class Node : public EventTarget, public ScriptWrappable, public TreeShared<Node> { +class Node : public EventTarget { + WTF_MAKE_FAST_ALLOCATED; + friend class Document; friend class TreeScope; friend class TreeScopeAdopter; - public: enum NodeType { ELEMENT_NODE = 1, ATTRIBUTE_NODE = 2, TEXT_NODE = 3, CDATA_SECTION_NODE = 4, - ENTITY_REFERENCE_NODE = 5, - ENTITY_NODE = 6, PROCESSING_INSTRUCTION_NODE = 7, COMMENT_NODE = 8, DOCUMENT_NODE = 9, DOCUMENT_TYPE_NODE = 10, DOCUMENT_FRAGMENT_NODE = 11, + }; + enum DeprecatedNodeType { + ENTITY_REFERENCE_NODE = 5, + ENTITY_NODE = 6, NOTATION_NODE = 12, - XPATH_NAMESPACE_NODE = 13, }; enum DocumentPosition { DOCUMENT_POSITION_EQUIVALENT = 0x00, @@ -146,31 +111,32 @@ public: DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20, }; - static bool isSupported(const String& feature, const String& version); - - static void startIgnoringLeaks(); - static void stopIgnoringLeaks(); + WEBCORE_EXPORT static void startIgnoringLeaks(); + WEBCORE_EXPORT static void stopIgnoringLeaks(); static void dumpStatistics(); virtual ~Node(); - void willBeDeletedFrom(Document*); + void willBeDeletedFrom(Document&); // DOM methods & attributes for Node - bool hasTagName(const QualifiedName&) const; - bool hasLocalName(const AtomicString&) const; + bool hasTagName(const HTMLQualifiedName&) const; + bool hasTagName(const MathMLQualifiedName&) const; + bool hasTagName(const SVGQualifiedName&) const; virtual String nodeName() const = 0; virtual String nodeValue() const; - virtual void setNodeValue(const String&, ExceptionCode&); + virtual ExceptionOr<void> setNodeValue(const String&); virtual NodeType nodeType() const = 0; + virtual size_t approximateMemoryCost() const { return sizeof(*this); } ContainerNode* parentNode() const; static ptrdiff_t parentNodeMemoryOffset() { return OBJECT_OFFSETOF(Node, m_parentNode); } Element* parentElement() const; Node* previousSibling() const { return m_previous; } static ptrdiff_t previousSiblingMemoryOffset() { return OBJECT_OFFSETOF(Node, m_previous); } Node* nextSibling() const { return m_next; } - PassRefPtr<NodeList> childNodes(); + static ptrdiff_t nextSiblingMemoryOffset() { return OBJECT_OFFSETOF(Node, m_next); } + WEBCORE_EXPORT RefPtr<NodeList> childNodes(); Node* firstChild() const; Node* lastChild() const; bool hasAttributes() const; @@ -180,40 +146,54 @@ public: Node* pseudoAwareFirstChild() const; Node* pseudoAwareLastChild() const; - virtual URL baseURI() const; + WEBCORE_EXPORT const URL& baseURI() const; void getSubresourceURLs(ListHashSet<URL>&) const; - // These should all actually return a node, but this is only important for language bindings, - // which will already know and hold a ref on the right node to return. Returning bool allows - // these methods to be more efficient since they don't need to return a ref - bool insertBefore(PassRefPtr<Node> newChild, Node* refChild, ExceptionCode&); - bool replaceChild(PassRefPtr<Node> newChild, Node* oldChild, ExceptionCode&); - bool removeChild(Node* child, ExceptionCode&); - bool appendChild(PassRefPtr<Node> newChild, ExceptionCode&); + WEBCORE_EXPORT ExceptionOr<void> insertBefore(Node& newChild, Node* refChild); + WEBCORE_EXPORT ExceptionOr<void> replaceChild(Node& newChild, Node& oldChild); + WEBCORE_EXPORT ExceptionOr<void> removeChild(Node& child); + WEBCORE_EXPORT ExceptionOr<void> appendChild(Node& newChild); - void remove(ExceptionCode&); bool hasChildNodes() const { return firstChild(); } - virtual PassRefPtr<Node> cloneNode(bool deep) = 0; + + enum class CloningOperation { + OnlySelf, + SelfWithTemplateContent, + Everything, + }; + virtual Ref<Node> cloneNodeInternal(Document&, CloningOperation) = 0; + Ref<Node> cloneNode(bool deep) { return cloneNodeInternal(document(), deep ? CloningOperation::Everything : CloningOperation::OnlySelf); } + WEBCORE_EXPORT ExceptionOr<Ref<Node>> cloneNodeForBindings(bool deep); + virtual const AtomicString& localName() const; virtual const AtomicString& namespaceURI() const; virtual const AtomicString& prefix() const; - virtual void setPrefix(const AtomicString&, ExceptionCode&); - void normalize(); + virtual ExceptionOr<void> setPrefix(const AtomicString&); + WEBCORE_EXPORT void normalize(); bool isSameNode(Node* other) const { return this == other; } - bool isEqualNode(Node*) const; - bool isDefaultNamespace(const AtomicString& namespaceURI) const; - String lookupPrefix(const AtomicString& namespaceURI) const; - String lookupNamespaceURI(const String& prefix) const; - String lookupNamespacePrefix(const AtomicString& namespaceURI, const Element* originalElement) const; - - String textContent(bool convertBRsToNewlines = false) const; - void setTextContent(const String&, ExceptionCode&); + WEBCORE_EXPORT bool isEqualNode(Node*) const; + WEBCORE_EXPORT bool isDefaultNamespace(const AtomicString& namespaceURI) const; + WEBCORE_EXPORT const AtomicString& lookupPrefix(const AtomicString& namespaceURI) const; + WEBCORE_EXPORT const AtomicString& lookupNamespaceURI(const AtomicString& prefix) const; + + WEBCORE_EXPORT String textContent(bool convertBRsToNewlines = false) const; + WEBCORE_EXPORT ExceptionOr<void> setTextContent(const String&); Node* lastDescendant() const; Node* firstDescendant() const; + // From the NonDocumentTypeChildNode - https://dom.spec.whatwg.org/#nondocumenttypechildnode + WEBCORE_EXPORT Element* previousElementSibling() const; + WEBCORE_EXPORT Element* nextElementSibling() const; + + // From the ChildNode - https://dom.spec.whatwg.org/#childnode + ExceptionOr<void> before(Vector<NodeOrString>&&); + ExceptionOr<void> after(Vector<NodeOrString>&&); + ExceptionOr<void> replaceWith(Vector<NodeOrString>&&); + WEBCORE_EXPORT ExceptionOr<void> remove(); + // Other methods (not part of DOM) bool isElementNode() const { return getFlag(IsElementFlag); } @@ -238,44 +218,60 @@ public: virtual bool isCharacterDataNode() const { return false; } virtual bool isFrameOwnerElement() const { return false; } virtual bool isPluginElement() const { return false; } - virtual bool isInsertionPointNode() const { return false; } +#if ENABLE(SERVICE_CONTROLS) + virtual bool isImageControlsRootElement() const { return false; } + virtual bool isImageControlsButtonElement() const { return false; } +#endif bool isDocumentNode() const; bool isTreeScope() const; bool isDocumentFragment() const { return getFlag(IsDocumentFragmentFlag); } bool isShadowRoot() const { return isDocumentFragment() && isTreeScope(); } - bool isInsertionPoint() const { return getFlag(NeedsNodeRenderingTraversalSlowPathFlag) && isInsertionPointNode(); } - // Returns Node rather than InsertionPoint. Should be used only for language bindings. - Node* insertionParentForBinding() const; - - bool needsNodeRenderingTraversalSlowPath() const; - bool inNamedFlow() const { return getFlag(InNamedFlowFlag); } bool hasCustomStyleResolveCallbacks() const { return getFlag(HasCustomStyleResolveCallbacksFlag); } bool hasSyntheticAttrChildNodes() const { return getFlag(HasSyntheticAttrChildNodesFlag); } void setHasSyntheticAttrChildNodes(bool flag) { setFlag(flag, HasSyntheticAttrChildNodesFlag); } - // If this node is in a shadow tree, returns its shadow host. Otherwise, returns 0. - Element* shadowHost() const; + // If this node is in a shadow tree, returns its shadow host. Otherwise, returns null. + WEBCORE_EXPORT Element* shadowHost() const; // If this node is in a shadow tree, returns its shadow host. Otherwise, returns this. // Deprecated. Should use shadowHost() and check the return value. - Node* deprecatedShadowAncestorNode() const; + WEBCORE_EXPORT Node* deprecatedShadowAncestorNode() const; ShadowRoot* containingShadowRoot() const; ShadowRoot* shadowRoot() const; + bool isClosedShadowHidden(const Node&) const; - // Returns 0, a child of ShadowRoot, or a legacy shadow root. + HTMLSlotElement* assignedSlot() const; + HTMLSlotElement* assignedSlotForBindings() const; + + bool isUndefinedCustomElement() const { return isElementNode() && getFlag(IsEditingTextOrUndefinedCustomElementFlag); } + bool isCustomElementUpgradeCandidate() const { return getFlag(IsCustomElement) && getFlag(IsEditingTextOrUndefinedCustomElementFlag); } + bool isDefinedCustomElement() const { return getFlag(IsCustomElement) && !getFlag(IsEditingTextOrUndefinedCustomElementFlag); } + bool isFailedCustomElement() const { return isElementNode() && !getFlag(IsCustomElement) && getFlag(IsEditingTextOrUndefinedCustomElementFlag); } + + // Returns null, a child of ShadowRoot, or a legacy shadow root. Node* nonBoundaryShadowTreeRootNode(); // Node's parent or shadow tree host. ContainerNode* parentOrShadowHostNode() const; + ContainerNode* parentInComposedTree() const; + Element* parentElementInComposedTree() const; Element* parentOrShadowHostElement() const; void setParentNode(ContainerNode*); - Node* highestAncestor() const; + Node& rootNode() const; + Node& shadowIncludingRoot() const; - // Use when it's guaranteed to that shadowHost is 0. + struct GetRootNodeOptions { + bool composed; + }; + Node& getRootNode(const GetRootNodeOptions&) const; + + void* opaqueRoot() const; + + // Use when it's guaranteed to that shadowHost is null. ContainerNode* parentNodeGuaranteedHostFree() const; - // Returns the parent node, but 0 if the parent node is a ShadowRoot. + // Returns the parent node, but null if the parent node is a ShadowRoot. ContainerNode* nonShadowBoundaryParentNode() const; bool selfOrAncestorHasDirAutoAttribute() const { return getFlag(SelfOrAncestorHasDirAutoFlag); } @@ -291,8 +287,7 @@ public: virtual bool canContainRangeEndPoint() const { return false; } bool isRootEditableElement() const; - Element* rootEditableElement() const; - Element* rootEditableElement(EditableType) const; + WEBCORE_EXPORT Element* rootEditableElement() const; // Called by the parser when this element's close tag is reached, // signaling that all child tags have been parsed and added. @@ -312,24 +307,20 @@ public: void setUserActionElement(bool flag) { setFlag(flag, IsUserActionElement); } bool inRenderedDocument() const; - bool needsStyleRecalc() const { return styleChangeType() != NoStyleChange; } - StyleChangeType styleChangeType() const { return static_cast<StyleChangeType>(m_nodeFlags & StyleChangeMask); } + bool needsStyleRecalc() const { return styleValidity() != Style::Validity::Valid; } + Style::Validity styleValidity() const; + bool styleResolutionShouldRecompositeLayer() const; bool childNeedsStyleRecalc() const { return getFlag(ChildNeedsStyleRecalcFlag); } - bool isLink() const { return getFlag(IsLinkFlag); } - bool isEditingText() const { return getFlag(IsEditingTextFlag); } + bool styleIsAffectedByPreviousSibling() const { return getFlag(StyleIsAffectedByPreviousSibling); } + bool isEditingText() const { return getFlag(IsTextFlag) && getFlag(IsEditingTextOrUndefinedCustomElementFlag); } void setChildNeedsStyleRecalc() { setFlag(ChildNeedsStyleRecalcFlag); } - void clearChildNeedsStyleRecalc() { clearFlag(ChildNeedsStyleRecalcFlag); } + void clearChildNeedsStyleRecalc() { m_nodeFlags &= ~(ChildNeedsStyleRecalcFlag | DirectChildNeedsStyleRecalcFlag); } - void setNeedsStyleRecalc(StyleChangeType changeType = FullStyleChange); - void clearNeedsStyleRecalc() { m_nodeFlags &= ~StyleChangeMask; } + void setHasValidStyle(); - void setIsLink(bool f) { setFlag(f, IsLinkFlag); } - void setIsLink() { setFlag(IsLinkFlag); } - void clearIsLink() { clearFlag(IsLinkFlag); } - - void setInNamedFlow() { setFlag(InNamedFlowFlag); } - void clearInNamedFlow() { clearFlag(InNamedFlowFlag); } + bool isLink() const { return getFlag(IsLinkFlag); } + void setIsLink(bool flag) { setFlag(flag, IsLinkFlag); } bool hasEventTargetData() const { return getFlag(HasEventTargetDataFlag); } void setHasEventTargetData(bool flag) { setFlag(flag, HasEventTargetDataFlag); } @@ -338,53 +329,39 @@ public: UserSelectAllDoesNotAffectEditability, UserSelectAllIsAlwaysNonEditable }; - bool isContentEditable(UserSelectAllTreatment = UserSelectAllDoesNotAffectEditability); + WEBCORE_EXPORT bool isContentEditable(); bool isContentRichlyEditable(); - void inspect(); + WEBCORE_EXPORT void inspect(); - bool hasEditableStyle(EditableType editableType = ContentIsEditable, UserSelectAllTreatment treatment = UserSelectAllIsAlwaysNonEditable) const + bool hasEditableStyle(UserSelectAllTreatment treatment = UserSelectAllIsAlwaysNonEditable) const { - switch (editableType) { - case ContentIsEditable: - return hasEditableStyle(Editable, treatment); - case HasEditableAXRole: - return isEditableToAccessibility(Editable); - } - ASSERT_NOT_REACHED(); - return false; + return computeEditability(treatment, ShouldUpdateStyle::DoNotUpdate) != Editability::ReadOnly; } - - bool hasRichlyEditableStyle(EditableType editableType = ContentIsEditable) const + // FIXME: Replace every use of this function by helpers in htmlediting.h + bool hasRichlyEditableStyle() const { - switch (editableType) { - case ContentIsEditable: - return hasEditableStyle(RichlyEditable, UserSelectAllIsAlwaysNonEditable); - case HasEditableAXRole: - return isEditableToAccessibility(RichlyEditable); - } - ASSERT_NOT_REACHED(); - return false; + return computeEditability(UserSelectAllIsAlwaysNonEditable, ShouldUpdateStyle::DoNotUpdate) == Editability::CanEditRichly; } - virtual LayoutRect boundingBox() const; - IntRect pixelSnappedBoundingBox() const { return pixelSnappedIntRect(boundingBox()); } - LayoutRect renderRect(bool* isReplaced); - IntRect pixelSnappedRenderRect(bool* isReplaced) { return pixelSnappedIntRect(renderRect(isReplaced)); } + enum class Editability { ReadOnly, CanEditPlainText, CanEditRichly }; + enum class ShouldUpdateStyle { Update, DoNotUpdate }; + WEBCORE_EXPORT Editability computeEditability(UserSelectAllTreatment, ShouldUpdateStyle) const; - unsigned nodeIndex() const; + WEBCORE_EXPORT LayoutRect renderRect(bool* isReplaced); + IntRect pixelSnappedRenderRect(bool* isReplaced) { return snappedIntRect(renderRect(isReplaced)); } - // Returns the DOM ownerDocument attribute. This method never returns 0, except in the case + WEBCORE_EXPORT unsigned computeNodeIndex() const; + + // Returns the DOM ownerDocument attribute. This method never returns null, except in the case // of a Document node. - Document* ownerDocument() const; + WEBCORE_EXPORT Document* ownerDocument() const; - // Returns the document associated with this node. This method never returns 0. + // Returns the document associated with this node. // A Document node returns itself. Document& document() const { - ASSERT(this); - ASSERT(documentInternal()); - return *documentInternal(); + return treeScope().documentScope(); } TreeScope& treeScope() const @@ -392,28 +369,30 @@ public: ASSERT(m_treeScope); return *m_treeScope; } + static ptrdiff_t treeScopeMemoryOffset() { return OBJECT_OFFSETOF(Node, m_treeScope); } // Returns true if this node is associated with a document and is in its associated document's - // node tree, false otherwise. - bool inDocument() const + // node tree, false otherwise (https://dom.spec.whatwg.org/#connected). + bool isConnected() const { - ASSERT(documentInternal() || !getFlag(InDocumentFlag)); - return getFlag(InDocumentFlag); + return getFlag(IsConnectedFlag); } + bool isInUserAgentShadowTree() const; bool isInShadowTree() const { return getFlag(IsInShadowTreeFlag); } - bool isInTreeScope() const { return getFlag(static_cast<NodeFlags>(InDocumentFlag | IsInShadowTreeFlag)); } + bool isInTreeScope() const { return getFlag(static_cast<NodeFlags>(IsConnectedFlag | IsInShadowTreeFlag)); } - bool isReadOnlyNode() const { return nodeType() == ENTITY_REFERENCE_NODE; } bool isDocumentTypeNode() const { return nodeType() == DOCUMENT_TYPE_NODE; } virtual bool childTypeAllowed(NodeType) const { return false; } - unsigned childNodeCount() const; - Node* childNode(unsigned index) const; + unsigned countChildNodes() const; + Node* traverseToChildAt(unsigned) const; + + ExceptionOr<void> checkSetPrefix(const AtomicString& prefix); - void checkSetPrefix(const AtomicString& prefix, ExceptionCode&); + WEBCORE_EXPORT bool isDescendantOf(const Node&) const; + bool isDescendantOf(const Node* other) const { return other && isDescendantOf(*other); } - bool isDescendantOf(const Node*) const; bool isDescendantOrShadowDescendantOf(const Node*) const; - bool contains(const Node*) const; + WEBCORE_EXPORT bool contains(const Node*) const; bool containsIncludingShadowDOM(const Node*) const; bool containsIncludingHostElements(const Node*) const; @@ -426,6 +405,8 @@ public: // Whether or not a selection can be started in this object virtual bool canStartSelection() const; + virtual bool shouldSelectOnMouseDown() { return false; } + // Getting points into and out of screen space FloatPoint convertToPage(const FloatPoint&) const; FloatPoint convertFromPage(const FloatPoint&) const; @@ -444,13 +425,13 @@ public: } // Use these two methods with caution. - RenderBox* renderBox() const; + WEBCORE_EXPORT RenderBox* renderBox() const; RenderBoxModelObject* renderBoxModelObject() const; // Wrapper for nodes that don't have a renderer, but still cache the style (like HTMLOptionElement). - RenderStyle* renderStyle() const; + const RenderStyle* renderStyle() const; - virtual RenderStyle* computedStyle(PseudoId pseudoElementSpecifier = NOPSEUDO); + virtual const RenderStyle* computedStyle(PseudoId pseudoElementSpecifier = NOPSEUDO); // ----------------------------------------------------------------------------- // Notification of document structure changes (see ContainerNode.h for more notification methods) @@ -461,20 +442,20 @@ public: // dispatching. // // WebKit notifies this callback regardless if the subtree of the node is a document tree or a floating subtree. - // Implementation can determine the type of subtree by seeing insertionPoint->inDocument(). + // Implementation can determine the type of subtree by seeing insertionPoint->isConnected(). // For a performance reason, notifications are delivered only to ContainerNode subclasses if the insertionPoint is out of document. // - // There are another callback named didNotifyDescendantInsertions(), which is called after all the descendant is notified. - // Only a few subclasses actually need this. To utilize this, the node should return InsertionShouldCallDidNotifyDescendantInsertions + // There is another callback named finishedInsertingSubtree(), which is called after all descendants are notified. + // Only a few subclasses actually need this. To utilize this, the node should return InsertionShouldCallFinishedInsertingSubtree // from insrtedInto(). // enum InsertionNotificationRequest { InsertionDone, - InsertionShouldCallDidNotifySubtreeInsertions + InsertionShouldCallFinishedInsertingSubtree }; virtual InsertionNotificationRequest insertedInto(ContainerNode& insertionPoint); - virtual void didNotifySubtreeInsertions(ContainerNode*) { } + virtual void finishedInsertingSubtree() { } // Notifies the node that it is no longer part of the tree. // @@ -483,17 +464,17 @@ public: // virtual void removedFrom(ContainerNode& insertionPoint); -#ifndef NDEBUG +#if ENABLE(TREE_DEBUGGING) virtual void formatForDebugger(char* buffer, unsigned length) const; void showNode(const char* prefix = "") const; void showTreeForThis() const; void showNodePathForThis() const; - void showTreeAndMark(const Node* markedNode1, const char* markedLabel1, const Node* markedNode2 = 0, const char* markedLabel2 = 0) const; + void showTreeAndMark(const Node* markedNode1, const char* markedLabel1, const Node* markedNode2 = nullptr, const char* markedLabel2 = nullptr) const; void showTreeForThisAcrossFrame() const; -#endif +#endif // ENABLE(TREE_DEBUGGING) - void invalidateNodeListAndCollectionCachesInAncestors(const QualifiedName* attrName = 0, Element* attributeOwnerElement = 0); + void invalidateNodeListAndCollectionCachesInAncestors(const QualifiedName* attrName = nullptr, Element* attributeOwnerElement = nullptr); NodeListsNodeData* nodeLists(); void clearNodeLists(); @@ -501,55 +482,63 @@ public: virtual bool willRespondToMouseClickEvents(); virtual bool willRespondToMouseWheelEvents(); - unsigned short compareDocumentPosition(Node*); + WEBCORE_EXPORT unsigned short compareDocumentPosition(Node&); - virtual Node* toNode() override; - virtual HTMLInputElement* toInputElement(); + Node* toNode() override; - virtual EventTargetInterface eventTargetInterface() const override; - virtual ScriptExecutionContext* scriptExecutionContext() const override final; // Implemented in Document.h + EventTargetInterface eventTargetInterface() const override; + ScriptExecutionContext* scriptExecutionContext() const final; // Implemented in Document.h - virtual bool addEventListener(const AtomicString& eventType, PassRefPtr<EventListener>, bool useCapture) override; - virtual bool removeEventListener(const AtomicString& eventType, EventListener*, bool useCapture) override; + bool addEventListener(const AtomicString& eventType, Ref<EventListener>&&, const AddEventListenerOptions&) override; + bool removeEventListener(const AtomicString& eventType, EventListener&, const ListenerOptions&) override; using EventTarget::dispatchEvent; - virtual bool dispatchEvent(PassRefPtr<Event>) override; + bool dispatchEvent(Event&) override; - void dispatchScopedEvent(PassRefPtr<Event>); + void dispatchScopedEvent(Event&); virtual void handleLocalEvents(Event&); void dispatchSubtreeModifiedEvent(); - bool dispatchDOMActivateEvent(int detail, PassRefPtr<Event> underlyingEvent); + bool dispatchDOMActivateEvent(int detail, Event& underlyingEvent); -#if ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS) - bool dispatchTouchEvent(PassRefPtr<TouchEvent>); -#endif -#if ENABLE(INDIE_UI) - bool dispatchUIRequestEvent(PassRefPtr<UIRequestEvent>); +#if ENABLE(TOUCH_EVENTS) + virtual bool allowsDoubleTapGesture() const { return true; } #endif +#if ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS) + bool dispatchTouchEvent(TouchEvent&); +#endif bool dispatchBeforeLoadEvent(const String& sourceURL); - virtual void dispatchInputEvent(); + void dispatchInputEvent(); // Perform the default action for an event. - virtual void defaultEventHandler(Event*); + virtual void defaultEventHandler(Event&); - using TreeShared<Node>::ref; - using TreeShared<Node>::deref; + void ref(); + void deref(); + bool hasOneRef() const; + int refCount() const; + +#ifndef NDEBUG + bool m_deletionHasBegun { false }; + bool m_inRemovedLastRefFunction { false }; + bool m_adoptionIsRequired { true }; +#endif - virtual EventTargetData* eventTargetData() override final; - virtual EventTargetData& ensureEventTargetData() override final; + EventTargetData* eventTargetData() final; + EventTargetData* eventTargetDataConcurrently() final; + EventTargetData& ensureEventTargetData() final; - void getRegisteredMutationObserversOfType(HashMap<MutationObserver*, MutationRecordDeliveryOptions>&, MutationObserver::MutationType, const QualifiedName* attributeName); - void registerMutationObserver(MutationObserver*, MutationObserverOptions, const HashSet<AtomicString>& attributeFilter); - void unregisterMutationObserver(MutationObserverRegistration*); - void registerTransientMutationObserver(MutationObserverRegistration*); - void unregisterTransientMutationObserver(MutationObserverRegistration*); + HashMap<MutationObserver*, MutationRecordDeliveryOptions> registeredMutationObservers(MutationObserver::MutationType, const QualifiedName* attributeName); + void registerMutationObserver(MutationObserver&, MutationObserverOptions, const HashSet<AtomicString>& attributeFilter); + void unregisterMutationObserver(MutationObserverRegistration&); + void registerTransientMutationObserver(MutationObserverRegistration&); + void unregisterTransientMutationObserver(MutationObserverRegistration&); void notifyMutationObserversNodeWillDetach(); - void textRects(Vector<IntRect>&) const; + WEBCORE_EXPORT void textRects(Vector<IntRect>&) const; unsigned connectedSubframeCount() const; void incrementConnectedSubframeCount(unsigned amount = 1); @@ -557,13 +546,23 @@ public: void updateAncestorConnectedSubframeCountForRemoval() const; void updateAncestorConnectedSubframeCountForInsertion() const; - void markAncestorsWithChildNeedsStyleRecalc(); - -#if ENABLE(CSS_SELECTOR_JIT) +#if ENABLE(JIT) static ptrdiff_t nodeFlagsMemoryOffset() { return OBJECT_OFFSETOF(Node, m_nodeFlags); } + static ptrdiff_t rareDataMemoryOffset() { return OBJECT_OFFSETOF(Node, m_data.m_rareData); } + static int32_t flagIsText() { return IsTextFlag; } + static int32_t flagIsContainer() { return IsContainerFlag; } static int32_t flagIsElement() { return IsElementFlag; } + static int32_t flagIsHTML() { return IsHTMLFlag; } static int32_t flagIsLink() { return IsLinkFlag; } -#endif // ENABLE(CSS_SELECTOR_JIT) + static int32_t flagHasFocusWithin() { return HasFocusWithin; } + static int32_t flagHasRareData() { return HasRareDataFlag; } + static int32_t flagIsParsingChildrenFinished() { return IsParsingChildrenFinishedFlag; } + static int32_t flagChildrenAffectedByFirstChildRulesFlag() { return ChildrenAffectedByFirstChildRulesFlag; } + static int32_t flagChildrenAffectedByLastChildRulesFlag() { return ChildrenAffectedByLastChildRulesFlag; } + + static int32_t flagAffectsNextSiblingElementStyle() { return AffectsNextSiblingElementStyle; } + static int32_t flagStyleIsAffectedByPreviousSibling() { return StyleIsAffectedByPreviousSibling; } +#endif // ENABLE(JIT) protected: enum NodeFlags { @@ -574,7 +573,7 @@ protected: IsHTMLFlag = 1 << 4, IsSVGFlag = 1 << 5, ChildNeedsStyleRecalcFlag = 1 << 7, - InDocumentFlag = 1 << 8, + IsConnectedFlag = 1 << 8, IsLinkFlag = 1 << 9, IsUserActionElement = 1 << 10, HasRareDataFlag = 1 << 11, @@ -583,23 +582,27 @@ protected: // These bits are used by derived classes, pulled up here so they can // be stored in the same memory word as the Node bits above. IsParsingChildrenFinishedFlag = 1 << 13, // Element - - StyleChangeMask = 1 << nodeStyleChangeShift | 1 << (nodeStyleChangeShift + 1) | 1 << (nodeStyleChangeShift + 2), - IsEditingTextFlag = 1 << 17, - InNamedFlowFlag = 1 << 18, + StyleValidityShift = 14, + StyleValidityMask = 3 << StyleValidityShift, + StyleResolutionShouldRecompositeLayerFlag = 1 << 16, + IsEditingTextOrUndefinedCustomElementFlag = 1 << 17, + HasFocusWithin = 1 << 18, HasSyntheticAttrChildNodesFlag = 1 << 19, HasCustomStyleResolveCallbacksFlag = 1 << 20, HasEventTargetDataFlag = 1 << 21, - NeedsNodeRenderingTraversalSlowPathFlag = 1 << 22, + IsCustomElement = 1 << 22, IsInShadowTreeFlag = 1 << 23, IsMathMLFlag = 1 << 24, ChildrenAffectedByFirstChildRulesFlag = 1 << 25, ChildrenAffectedByLastChildRulesFlag = 1 << 26, - ChildrenAffectedByDirectAdjacentRulesFlag = 1 << 27, - ChildrenAffectedByHoverRulesFlag = 1 << 28, + ChildrenAffectedByHoverRulesFlag = 1 << 27, + + DirectChildNeedsStyleRecalcFlag = 1 << 28, + AffectsNextSiblingElementStyle = 1 << 29, + StyleIsAffectedByPreviousSibling = 1 << 30, - SelfOrAncestorHasDirAutoFlag = 1 << 29, + SelfOrAncestorHasDirAutoFlag = 1 << 31, DefaultNodeFlags = IsParsingChildrenFinishedFlag }; @@ -614,20 +617,19 @@ protected: CreateText = DefaultNodeFlags | IsTextFlag, CreateContainer = DefaultNodeFlags | IsContainerFlag, CreateElement = CreateContainer | IsElementFlag, - CreatePseudoElement = CreateElement | InDocumentFlag | NeedsNodeRenderingTraversalSlowPathFlag, - CreateShadowRoot = CreateContainer | IsDocumentFragmentFlag | NeedsNodeRenderingTraversalSlowPathFlag | IsInShadowTreeFlag, + CreatePseudoElement = CreateElement | IsConnectedFlag, + CreateShadowRoot = CreateContainer | IsDocumentFragmentFlag | IsInShadowTreeFlag, CreateDocumentFragment = CreateContainer | IsDocumentFragmentFlag, CreateStyledElement = CreateElement | IsStyledElementFlag, CreateHTMLElement = CreateStyledElement | IsHTMLFlag, CreateSVGElement = CreateStyledElement | IsSVGFlag | HasCustomStyleResolveCallbacksFlag, - CreateDocument = CreateContainer | InDocumentFlag, - CreateInsertionPoint = CreateHTMLElement | NeedsNodeRenderingTraversalSlowPathFlag, - CreateEditingText = CreateText | IsEditingTextFlag, - CreateMathMLElement = CreateStyledElement | IsMathMLFlag, + CreateDocument = CreateContainer | IsConnectedFlag, + CreateEditingText = CreateText | IsEditingTextOrUndefinedCustomElementFlag, + CreateMathMLElement = CreateStyledElement | IsMathMLFlag }; - Node(Document*, ConstructionType); + Node(Document&, ConstructionType); - virtual void didMoveToNewDocument(Document* oldDocument); + virtual void didMoveToNewDocument(Document& oldDocument); virtual void addSubresourceAttributeURLs(ListHashSet<URL>&) const { } @@ -641,52 +643,47 @@ protected: void setHasCustomStyleResolveCallbacks() { setFlag(true, HasCustomStyleResolveCallbacksFlag); } - void setNeedsNodeRenderingTraversalSlowPath(bool flag) { setFlag(flag, NeedsNodeRenderingTraversalSlowPathFlag); } - - Document* documentInternal() const { return treeScope().documentScope(); } void setTreeScope(TreeScope& scope) { m_treeScope = &scope; } - void setStyleChange(StyleChangeType changeType) { m_nodeFlags = (m_nodeFlags & ~StyleChangeMask) | changeType; } + void invalidateStyle(Style::Validity, Style::InvalidationMode = Style::InvalidationMode::Normal); + void updateAncestorsForStyleRecalc(); -private: - friend class TreeShared<Node>; + ExceptionOr<RefPtr<Node>> convertNodesOrStringsIntoNode(Vector<NodeOrString>&&); +private: virtual PseudoId customPseudoId() const { ASSERT(hasCustomStyleResolveCallbacks()); return NOPSEUDO; } - void removedLastRef(); - bool hasTreeSharedParent() const { return !!parentNode(); } + WEBCORE_EXPORT void removedLastRef(); - enum EditableLevel { Editable, RichlyEditable }; - bool hasEditableStyle(EditableLevel, UserSelectAllTreatment = UserSelectAllIsAlwaysNonEditable) const; - bool isEditableToAccessibility(EditableLevel) const; - - virtual void refEventTarget() override; - virtual void derefEventTarget() override; - - virtual RenderStyle* nonRendererStyle() const { return 0; } - - Element* ancestorElement() const; + void refEventTarget() override; + void derefEventTarget() override; void trackForDebugging(); + void materializeRareData(); - Vector<OwnPtr<MutationObserverRegistration>>* mutationObserverRegistry(); + Vector<std::unique_ptr<MutationObserverRegistration>>* mutationObserverRegistry(); HashSet<MutationObserverRegistration*>* transientMutationObserverRegistry(); + void adjustStyleValidity(Style::Validity, Style::InvalidationMode); + + void* opaqueRootSlow() const; + + int m_refCount; mutable uint32_t m_nodeFlags; - ContainerNode* m_parentNode; - TreeScope* m_treeScope; - Node* m_previous; - Node* m_next; + + ContainerNode* m_parentNode { nullptr }; + TreeScope* m_treeScope { nullptr }; + Node* m_previous { nullptr }; + Node* m_next { nullptr }; // When a node has rare data we move the renderer into the rare data. union DataUnion { - DataUnion() : m_renderer(0) { } RenderObject* m_renderer; NodeRareDataBase* m_rareData; - } m_data; + } m_data { nullptr }; protected: bool isParsingChildrenFinished() const { return getFlag(IsParsingChildrenFinishedFlag); } @@ -694,6 +691,53 @@ protected: void clearIsParsingChildrenFinished() { clearFlag(IsParsingChildrenFinishedFlag); } }; +#ifndef NDEBUG +inline void adopted(Node* node) +{ + if (!node) + return; + ASSERT(!node->m_deletionHasBegun); + ASSERT(!node->m_inRemovedLastRefFunction); + node->m_adoptionIsRequired = false; +} +#endif + +ALWAYS_INLINE void Node::ref() +{ + ASSERT(isMainThread()); + ASSERT(!m_deletionHasBegun); + ASSERT(!m_inRemovedLastRefFunction); + ASSERT(!m_adoptionIsRequired); + ++m_refCount; +} + +ALWAYS_INLINE void Node::deref() +{ + ASSERT(isMainThread()); + ASSERT(m_refCount >= 0); + ASSERT(!m_deletionHasBegun); + ASSERT(!m_inRemovedLastRefFunction); + ASSERT(!m_adoptionIsRequired); + if (--m_refCount <= 0 && !parentNode()) { +#ifndef NDEBUG + m_inRemovedLastRefFunction = true; +#endif + removedLastRef(); + } +} + +ALWAYS_INLINE bool Node::hasOneRef() const +{ + ASSERT(!m_deletionHasBegun); + ASSERT(!m_inRemovedLastRefFunction); + return m_refCount == 1; +} + +ALWAYS_INLINE int Node::refCount() const +{ + return m_refCount; +} + // Used in Node::addSubresourceAttributeURLs() and in addSubresourceStyleURLs() inline void addSubresourceURL(ListHashSet<URL>& urls, const URL& url) { @@ -713,21 +757,41 @@ inline ContainerNode* Node::parentNode() const return m_parentNode; } +inline void* Node::opaqueRoot() const +{ + // FIXME: Possible race? + // https://bugs.webkit.org/show_bug.cgi?id=165713 + if (isConnected()) + return &document(); + return opaqueRootSlow(); +} + inline ContainerNode* Node::parentNodeGuaranteedHostFree() const { ASSERT(!isShadowRoot()); return parentNode(); } -#define NODE_TYPE_CASTS(ToClassName) \ - TYPE_CASTS_BASE(ToClassName, Node, node, WebCore::is##ToClassName(*node), WebCore::is##ToClassName(node)) +inline Style::Validity Node::styleValidity() const +{ + return static_cast<Style::Validity>((m_nodeFlags & StyleValidityMask) >> StyleValidityShift); +} + +inline bool Node::styleResolutionShouldRecompositeLayer() const +{ + return getFlag(StyleResolutionShouldRecompositeLayerFlag); +} + +inline void Node::setHasValidStyle() +{ + m_nodeFlags &= ~StyleValidityMask; + clearFlag(StyleResolutionShouldRecompositeLayerFlag); +} } // namespace WebCore -#ifndef NDEBUG -// Outside the WebCore namespace for ease of invocation from gdb. +#if ENABLE(TREE_DEBUGGING) +// Outside the WebCore namespace for ease of invocation from the debugger. void showTree(const WebCore::Node*); void showNodePath(const WebCore::Node*); #endif - -#endif diff --git a/Source/WebCore/dom/Node.idl b/Source/WebCore/dom/Node.idl index 12b304505..d738f2a3d 100644 --- a/Source/WebCore/dom/Node.idl +++ b/Source/WebCore/dom/Node.idl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2006-2016 Apple Inc. All rights reserved. * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> * * This library is free software; you can redistribute it and/or @@ -19,130 +19,80 @@ */ [ + CustomIsReachable, + CustomToJSObject, + ExportMacro=WEBCORE_EXPORT, JSCustomHeader, JSCustomMarkFunction, JSCustomPushEventHandlerScope, - CustomIsReachable, - CustomToJSObject, - EventTarget, - JSGenerateToNativeObject, - ObjCPolymorphic, -] interface Node -#if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C - : Object, EventTarget -#endif /* defined(LANGUAGE_OBJECTIVE_C) */ - { +] interface Node : EventTarget { // NodeType - const unsigned short ELEMENT_NODE = 1; - const unsigned short ATTRIBUTE_NODE = 2; - const unsigned short TEXT_NODE = 3; - const unsigned short CDATA_SECTION_NODE = 4; - const unsigned short ENTITY_REFERENCE_NODE = 5; - const unsigned short ENTITY_NODE = 6; - const unsigned short PROCESSING_INSTRUCTION_NODE = 7; - const unsigned short COMMENT_NODE = 8; - const unsigned short DOCUMENT_NODE = 9; - const unsigned short DOCUMENT_TYPE_NODE = 10; - const unsigned short DOCUMENT_FRAGMENT_NODE = 11; - const unsigned short NOTATION_NODE = 12; - - [TreatReturnedNullStringAs=Null] readonly attribute DOMString nodeName; - - // FIXME: the spec says this can also raise on retrieval. - [TreatReturnedNullStringAs=Null, TreatNullAs=NullString, SetterRaisesException] attribute DOMString nodeValue; - - readonly attribute unsigned short nodeType; - readonly attribute Node parentNode; - readonly attribute NodeList childNodes; - readonly attribute Node firstChild; - readonly attribute Node lastChild; - readonly attribute Node previousSibling; - readonly attribute Node nextSibling; - readonly attribute Document ownerDocument; - - [ObjCLegacyUnnamedParameters, Custom, RaisesException] Node insertBefore([CustomReturn] Node newChild, - Node refChild); - [ObjCLegacyUnnamedParameters, Custom, RaisesException] Node replaceChild(Node newChild, - [CustomReturn] Node oldChild); - [Custom, RaisesException] Node removeChild([CustomReturn] Node oldChild); - [Custom, RaisesException] Node appendChild([CustomReturn] Node newChild); - - boolean hasChildNodes(); - Node cloneNode([Default=Undefined] optional boolean deep); - void normalize(); - - // Introduced in DOM Level 2: - - [ObjCLegacyUnnamedParameters] boolean isSupported([Default=Undefined] optional DOMString feature, - [TreatNullAs=NullString,Default=Undefined] optional DOMString version); - - [TreatReturnedNullStringAs=Null] readonly attribute DOMString namespaceURI; - [TreatReturnedNullStringAs=Null, TreatNullAs=NullString, SetterRaisesException] attribute DOMString prefix; - [TreatReturnedNullStringAs=Null] readonly attribute DOMString localName; - -#if defined(LANGUAGE_OBJECTIVE_C) - readonly attribute NamedNodeMap attributes; - boolean hasAttributes(); -#endif - - - // Introduced in DOM Level 3: - - [TreatReturnedNullStringAs=Null] readonly attribute DOMString baseURI; - - // FIXME: the spec says this can also raise on retrieval. - [TreatReturnedNullStringAs=Null, TreatNullAs=NullString, SetterRaisesException] attribute DOMString textContent; - - boolean isSameNode([Default=Undefined] optional Node other); - boolean isEqualNode([Default=Undefined] optional Node other); - [TreatReturnedNullStringAs=Null] DOMString lookupPrefix([TreatNullAs=NullString,Default=Undefined] optional DOMString namespaceURI); - boolean isDefaultNamespace([TreatNullAs=NullString,Default=Undefined] optional DOMString namespaceURI); - [TreatReturnedNullStringAs=Null] DOMString lookupNamespaceURI([TreatNullAs=NullString,Default=Undefined] optional DOMString prefix); + const unsigned short ELEMENT_NODE = 1; + const unsigned short ATTRIBUTE_NODE = 2; + const unsigned short TEXT_NODE = 3; + const unsigned short CDATA_SECTION_NODE = 4; + const unsigned short ENTITY_REFERENCE_NODE = 5; + const unsigned short ENTITY_NODE = 6; + const unsigned short PROCESSING_INSTRUCTION_NODE = 7; + const unsigned short COMMENT_NODE = 8; + const unsigned short DOCUMENT_NODE = 9; + const unsigned short DOCUMENT_TYPE_NODE = 10; + const unsigned short DOCUMENT_FRAGMENT_NODE = 11; + const unsigned short NOTATION_NODE = 12; + + readonly attribute DOMString nodeName; + + [CEReactions, SetterMayThrowException] attribute DOMString? nodeValue; + + [DOMJIT=Getter, CustomGetter] readonly attribute unsigned short nodeType; + [DOMJIT=Getter] readonly attribute Node? parentNode; + readonly attribute NodeList childNodes; + [DOMJIT=Getter] readonly attribute Node? firstChild; + [DOMJIT=Getter] readonly attribute Node? lastChild; + [DOMJIT=Getter] readonly attribute Node? previousSibling; + [DOMJIT=Getter] readonly attribute Node? nextSibling; + [DOMJIT=Getter] readonly attribute Document? ownerDocument; + + [CEReactions, Custom, MayThrowException] Node insertBefore(Node newChild, Node? refChild); + [CEReactions, Custom, MayThrowException] Node replaceChild(Node newChild, Node oldChild); + [CEReactions, Custom, MayThrowException] Node removeChild(Node oldChild); + [CEReactions, Custom, MayThrowException] Node appendChild(Node newChild); + + boolean hasChildNodes(); + + [CEReactions, ImplementedAs=cloneNodeForBindings, MayThrowException, NewObject] Node cloneNode(optional boolean deep = false); + + void normalize(); + + readonly attribute USVString baseURI; + + [CEReactions, SetterMayThrowException] attribute DOMString? textContent; + + boolean isSameNode(Node? other); + boolean isEqualNode(Node? other); + + DOMString? lookupPrefix(DOMString? namespaceURI); + DOMString? lookupNamespaceURI(DOMString? prefix); + boolean isDefaultNamespace(DOMString? namespaceURI); // DocumentPosition - const unsigned short DOCUMENT_POSITION_DISCONNECTED = 0x01; - const unsigned short DOCUMENT_POSITION_PRECEDING = 0x02; - const unsigned short DOCUMENT_POSITION_FOLLOWING = 0x04; - const unsigned short DOCUMENT_POSITION_CONTAINS = 0x08; - const unsigned short DOCUMENT_POSITION_CONTAINED_BY = 0x10; - const unsigned short DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20; - - unsigned short compareDocumentPosition([Default=Undefined] optional Node other); - - // Introduced in DOM4 - boolean contains([Default=Undefined] optional Node other); - - // IE extensions - readonly attribute Element parentElement; - -#if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C - // Objective-C extensions - readonly attribute boolean isContentEditable; - - void inspect(); -#endif /* defined(LANGUAGE_OBJECTIVE_C) */ - -#if !defined(LANGUAGE_CPP) || !LANGUAGE_CPP -#if !defined(LANGUAGE_OBJECTIVE_C) || !LANGUAGE_OBJECTIVE_C - void addEventListener(DOMString type, - EventListener listener, - optional boolean useCapture); - void removeEventListener(DOMString type, - EventListener listener, - optional boolean useCapture); - [RaisesException] boolean dispatchEvent(Event event); -#endif -#endif - -#if defined(LANGUAGE_CPP) && LANGUAGE_CPP - [Custom] void addEventListener(DOMString type, - EventListener listener, - boolean useCapture); - [Custom] void removeEventListener(DOMString type, - EventListener listener, - boolean useCapture); - [RaisesException] boolean dispatchEvent(Event event); -#endif + const unsigned short DOCUMENT_POSITION_DISCONNECTED = 0x01; + const unsigned short DOCUMENT_POSITION_PRECEDING = 0x02; + const unsigned short DOCUMENT_POSITION_FOLLOWING = 0x04; + const unsigned short DOCUMENT_POSITION_CONTAINS = 0x08; + const unsigned short DOCUMENT_POSITION_CONTAINED_BY = 0x10; + const unsigned short DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20; + unsigned short compareDocumentPosition(Node other); + + boolean contains(Node? other); + + [EnabledAtRuntime=ShadowDOM] Node getRootNode(optional GetRootNodeOptions options); + [EnabledAtRuntime=ShadowDOM] readonly attribute boolean isConnected; + + readonly attribute Element parentElement; }; +dictionary GetRootNodeOptions { + boolean composed = false; +}; diff --git a/Source/WebCore/dom/NodeConstants.h b/Source/WebCore/dom/NodeConstants.h new file mode 100644 index 000000000..ebbdc7812 --- /dev/null +++ b/Source/WebCore/dom/NodeConstants.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +namespace WebCore { + +struct NodeConstants { + enum NodeType { + ELEMENT_NODE = 1, + ATTRIBUTE_NODE = 2, + TEXT_NODE = 3, + CDATA_SECTION_NODE = 4, + PROCESSING_INSTRUCTION_NODE = 7, + COMMENT_NODE = 8, + DOCUMENT_NODE = 9, + DOCUMENT_TYPE_NODE = 10, + DOCUMENT_FRAGMENT_NODE = 11, + }; + + enum DeprecatedNodeType { + ENTITY_REFERENCE_NODE = 5, + ENTITY_NODE = 6, + NOTATION_NODE = 12, + }; + + static const uint8_t LastNodeType = NOTATION_NODE; +}; + +} // namespace WebCore::NodeType diff --git a/Source/WebCore/dom/NodeFilter.cpp b/Source/WebCore/dom/NodeFilter.cpp deleted file mode 100644 index 8dabc540a..000000000 --- a/Source/WebCore/dom/NodeFilter.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 1999 Lars Knoll (knoll@kde.org) - * Copyright (C) 2000 Frederik Holljen (frederik.holljen@hig.no) - * Copyright (C) 2001 Peter Kelly (pmk@post.com) - * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - * Copyright (C) 2004, 2008 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#include "config.h" -#include "NodeFilter.h" - -#include "Node.h" - -namespace WebCore { - -short NodeFilter::acceptNode(JSC::ExecState* state, Node* node) const -{ - // cast to short silences "enumeral and non-enumeral types in return" warning - return m_condition ? m_condition->acceptNode(state, node) : static_cast<short>(FILTER_ACCEPT); -} - -} // namespace WebCore diff --git a/Source/WebCore/dom/NodeFilter.h b/Source/WebCore/dom/NodeFilter.h index 1aad6fe02..04372a1e5 100644 --- a/Source/WebCore/dom/NodeFilter.h +++ b/Source/WebCore/dom/NodeFilter.h @@ -3,7 +3,7 @@ * Copyright (C) 2000 Frederik Holljen (frederik.holljen@hig.no) * Copyright (C) 2001 Peter Kelly (pmk@post.com) * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - * Copyright (C) 2004, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2008, 2009, 2015 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -22,74 +22,50 @@ * */ -#ifndef NodeFilter_h -#define NodeFilter_h +#pragma once -#include "DOMWrapperWorld.h" -#include "NodeFilterCondition.h" -#include <wtf/RefPtr.h> +#include <wtf/RefCounted.h> namespace WebCore { - class NodeFilter : public RefCounted<NodeFilter> { - public: - /** - * The following constants are returned by the acceptNode() - * method: - */ - enum { - FILTER_ACCEPT = 1, - FILTER_REJECT = 2, - FILTER_SKIP = 3 - }; +class Node; - /** - * These are the available values for the whatToShow parameter. - * They are the same as the set of possible types for Node, and - * their values are derived by using a bit position corresponding - * to the value of NodeType for the equivalent node type. - */ - enum { - SHOW_ALL = 0xFFFFFFFF, - SHOW_ELEMENT = 0x00000001, - SHOW_ATTRIBUTE = 0x00000002, - SHOW_TEXT = 0x00000004, - SHOW_CDATA_SECTION = 0x00000008, - SHOW_ENTITY_REFERENCE = 0x00000010, - SHOW_ENTITY = 0x00000020, - SHOW_PROCESSING_INSTRUCTION = 0x00000040, - SHOW_COMMENT = 0x00000080, - SHOW_DOCUMENT = 0x00000100, - SHOW_DOCUMENT_TYPE = 0x00000200, - SHOW_DOCUMENT_FRAGMENT = 0x00000400, - SHOW_NOTATION = 0x00000800 - }; +class NodeFilter : public RefCounted<NodeFilter> { +public: + virtual ~NodeFilter() { } + virtual uint16_t acceptNode(Node*) = 0; - static PassRefPtr<NodeFilter> create(PassRefPtr<NodeFilterCondition> condition) - { - return adoptRef(new NodeFilter(condition)); - } - - static PassRefPtr<NodeFilter> create() - { - return adoptRef(new NodeFilter()); - } - - short acceptNode(JSC::ExecState*, Node*) const; - - // Do not call these functions. They are just scaffolding to support the Objective-C bindings. - // They operate in the main thread normal world, and they swallow JS exceptions. - short acceptNode(Node* node) const { return acceptNode(execStateFromNode(mainThreadNormalWorld(), node), node); } - - void setCondition(PassRefPtr<NodeFilterCondition> condition) { ASSERT(!m_condition); m_condition = condition; } - - private: - explicit NodeFilter(PassRefPtr<NodeFilterCondition> condition) : m_condition(condition) { } - NodeFilter() {} + /* + * The following constants are returned by the acceptNode() + * method: + */ + enum { + FILTER_ACCEPT = 1, + FILTER_REJECT = 2, + FILTER_SKIP = 3 + }; - RefPtr<NodeFilterCondition> m_condition; + /* + * These are the available values for the whatToShow parameter. + * They are the same as the set of possible types for Node, and + * their values are derived by using a bit position corresponding + * to the value of NodeType for the equivalent node type. + */ + enum : unsigned long { + SHOW_ALL = 0xFFFFFFFF, + SHOW_ELEMENT = 0x00000001, + SHOW_ATTRIBUTE = 0x00000002, + SHOW_TEXT = 0x00000004, + SHOW_CDATA_SECTION = 0x00000008, + SHOW_ENTITY_REFERENCE = 0x00000010, + SHOW_ENTITY = 0x00000020, + SHOW_PROCESSING_INSTRUCTION = 0x00000040, + SHOW_COMMENT = 0x00000080, + SHOW_DOCUMENT = 0x00000100, + SHOW_DOCUMENT_TYPE = 0x00000200, + SHOW_DOCUMENT_FRAGMENT = 0x00000400, + SHOW_NOTATION = 0x00000800 }; +}; } // namespace WebCore - -#endif // NodeFilter_h diff --git a/Source/WebCore/dom/NodeFilter.idl b/Source/WebCore/dom/NodeFilter.idl index deee0ab63..7330b3b96 100644 --- a/Source/WebCore/dom/NodeFilter.idl +++ b/Source/WebCore/dom/NodeFilter.idl @@ -18,35 +18,28 @@ * Boston, MA 02110-1301, USA. */ -// Introduced in DOM Level 2: [ - JSCustomMarkFunction, - JSCustomToNativeObject, - ObjCProtocol, - CPPPureInterface, - ImplementationLacksVTable, -] interface NodeFilter { + IsWeakCallback, +] callback interface NodeFilter { // Constants returned by acceptNode - const short FILTER_ACCEPT = 1; - const short FILTER_REJECT = 2; - const short FILTER_SKIP = 3; + const unsigned short FILTER_ACCEPT = 1; + const unsigned short FILTER_REJECT = 2; + const unsigned short FILTER_SKIP = 3; // Constants for whatToShow - const unsigned long SHOW_ALL = 0xFFFFFFFF; - const unsigned long SHOW_ELEMENT = 0x00000001; - const unsigned long SHOW_ATTRIBUTE = 0x00000002; - const unsigned long SHOW_TEXT = 0x00000004; - const unsigned long SHOW_CDATA_SECTION = 0x00000008; - const unsigned long SHOW_ENTITY_REFERENCE = 0x00000010; - const unsigned long SHOW_ENTITY = 0x00000020; - const unsigned long SHOW_PROCESSING_INSTRUCTION = 0x00000040; - const unsigned long SHOW_COMMENT = 0x00000080; - const unsigned long SHOW_DOCUMENT = 0x00000100; - const unsigned long SHOW_DOCUMENT_TYPE = 0x00000200; - const unsigned long SHOW_DOCUMENT_FRAGMENT = 0x00000400; - const unsigned long SHOW_NOTATION = 0x00000800; - - [CallWith=ScriptState] short acceptNode([Default=Undefined] optional Node n); + const unsigned long SHOW_ALL = 0xFFFFFFFF; + const unsigned long SHOW_ELEMENT = 0x00000001; + const unsigned long SHOW_ATTRIBUTE = 0x00000002; + const unsigned long SHOW_TEXT = 0x00000004; + const unsigned long SHOW_CDATA_SECTION = 0x00000008; + const unsigned long SHOW_ENTITY_REFERENCE = 0x00000010; + const unsigned long SHOW_ENTITY = 0x00000020; + const unsigned long SHOW_PROCESSING_INSTRUCTION = 0x00000040; + const unsigned long SHOW_COMMENT = 0x00000080; + const unsigned long SHOW_DOCUMENT = 0x00000100; + const unsigned long SHOW_DOCUMENT_TYPE = 0x00000200; + const unsigned long SHOW_DOCUMENT_FRAGMENT = 0x00000400; + const unsigned long SHOW_NOTATION = 0x00000800; + [Custom] unsigned short acceptNode(optional Node node); }; - diff --git a/Source/WebCore/dom/NodeFilterCondition.cpp b/Source/WebCore/dom/NodeFilterCondition.cpp index e478d948a..72c9ee126 100644 --- a/Source/WebCore/dom/NodeFilterCondition.cpp +++ b/Source/WebCore/dom/NodeFilterCondition.cpp @@ -29,7 +29,7 @@ namespace WebCore { -short NodeFilterCondition::acceptNode(JSC::ExecState*, Node*) const +short NodeFilterCondition::acceptNode(Node*) const { return NodeFilter::FILTER_ACCEPT; } diff --git a/Source/WebCore/dom/NodeFilterCondition.h b/Source/WebCore/dom/NodeFilterCondition.h index 8ba38f699..8ccebe300 100644 --- a/Source/WebCore/dom/NodeFilterCondition.h +++ b/Source/WebCore/dom/NodeFilterCondition.h @@ -22,29 +22,23 @@ * */ -#ifndef NodeFilterCondition_h -#define NodeFilterCondition_h +#pragma once -#include "ScriptState.h" #include <wtf/RefCounted.h> namespace JSC { - class SlotVisitor; - } namespace WebCore { - class Node; +class Node; - class NodeFilterCondition : public RefCounted<NodeFilterCondition> { - public: - virtual ~NodeFilterCondition() { } - virtual short acceptNode(JSC::ExecState*, Node*) const = 0; - virtual void visitAggregate(JSC::SlotVisitor&) { } - }; +class NodeFilterCondition : public RefCounted<NodeFilterCondition> { +public: + virtual ~NodeFilterCondition() { } + virtual short acceptNode(Node*) const = 0; + virtual void visitAggregate(JSC::SlotVisitor&) { } +}; } // namespace WebCore - -#endif // NodeFilterCondition_h diff --git a/Source/WebCore/dom/NodeIterator.cpp b/Source/WebCore/dom/NodeIterator.cpp index 082fdc6f7..cd6957a1e 100644 --- a/Source/WebCore/dom/NodeIterator.cpp +++ b/Source/WebCore/dom/NodeIterator.cpp @@ -26,27 +26,23 @@ #include "NodeIterator.h" #include "Document.h" -#include "ExceptionCode.h" #include "NodeTraversal.h" +#include <runtime/JSCJSValueInlines.h> namespace WebCore { -NodeIterator::NodePointer::NodePointer() +inline NodeIterator::NodePointer::NodePointer(Node& node, bool isPointerBeforeNode) + : node(&node) + , isPointerBeforeNode(isPointerBeforeNode) { } -NodeIterator::NodePointer::NodePointer(PassRefPtr<Node> n, bool b) - : node(n) - , isPointerBeforeNode(b) +inline void NodeIterator::NodePointer::clear() { + node = nullptr; } -void NodeIterator::NodePointer::clear() -{ - node.clear(); -} - -bool NodeIterator::NodePointer::moveToNext(Node* root) +inline bool NodeIterator::NodePointer::moveToNext(Node& root) { if (!node) return false; @@ -54,11 +50,11 @@ bool NodeIterator::NodePointer::moveToNext(Node* root) isPointerBeforeNode = false; return true; } - node = NodeTraversal::next(node.get(), root); + node = NodeTraversal::next(*node, &root); return node; } -bool NodeIterator::NodePointer::moveToPrevious(Node* root) +inline bool NodeIterator::NodePointer::moveToPrevious(Node& root) { if (!node) return false; @@ -66,34 +62,33 @@ bool NodeIterator::NodePointer::moveToPrevious(Node* root) isPointerBeforeNode = true; return true; } - if (node == root) { + if (node == &root) { node = nullptr; return false; } - node = NodeTraversal::previous(node.get()); + node = NodeTraversal::previous(*node); return node; } -NodeIterator::NodeIterator(PassRefPtr<Node> rootNode, unsigned whatToShow, PassRefPtr<NodeFilter> filter, bool expandEntityReferences) - : NodeIteratorBase(rootNode, whatToShow, filter, expandEntityReferences) - , m_referenceNode(root(), true) - , m_detached(false) +inline NodeIterator::NodeIterator(Node& rootNode, unsigned whatToShow, RefPtr<NodeFilter>&& filter) + : NodeIteratorBase(rootNode, whatToShow, WTFMove(filter)) + , m_referenceNode(rootNode, true) { - root()->document().attachNodeIterator(this); + root().document().attachNodeIterator(this); } -NodeIterator::~NodeIterator() +Ref<NodeIterator> NodeIterator::create(Node& rootNode, unsigned whatToShow, RefPtr<NodeFilter>&& filter) { - root()->document().detachNodeIterator(this); + return adoptRef(*new NodeIterator(rootNode, whatToShow, WTFMove(filter))); } -PassRefPtr<Node> NodeIterator::nextNode(JSC::ExecState* state, ExceptionCode& ec) +NodeIterator::~NodeIterator() { - if (m_detached) { - ec = INVALID_STATE_ERR; - return 0; - } + root().document().detachNodeIterator(this); +} +RefPtr<Node> NodeIterator::nextNode() +{ RefPtr<Node> result; m_candidateNode = m_referenceNode; @@ -102,27 +97,20 @@ PassRefPtr<Node> NodeIterator::nextNode(JSC::ExecState* state, ExceptionCode& ec // In other words, FILTER_REJECT does not pass over descendants // of the rejected node. Hence, FILTER_REJECT is the same as FILTER_SKIP. RefPtr<Node> provisionalResult = m_candidateNode.node; - bool nodeWasAccepted = acceptNode(state, provisionalResult.get()) == NodeFilter::FILTER_ACCEPT; - if (state && state->hadException()) - break; + bool nodeWasAccepted = acceptNode(provisionalResult.get()) == NodeFilter::FILTER_ACCEPT; if (nodeWasAccepted) { m_referenceNode = m_candidateNode; - result = provisionalResult.release(); + result = WTFMove(provisionalResult); break; } } m_candidateNode.clear(); - return result.release(); + return result; } -PassRefPtr<Node> NodeIterator::previousNode(JSC::ExecState* state, ExceptionCode& ec) +RefPtr<Node> NodeIterator::previousNode() { - if (m_detached) { - ec = INVALID_STATE_ERR; - return 0; - } - RefPtr<Node> result; m_candidateNode = m_referenceNode; @@ -131,55 +119,44 @@ PassRefPtr<Node> NodeIterator::previousNode(JSC::ExecState* state, ExceptionCode // In other words, FILTER_REJECT does not pass over descendants // of the rejected node. Hence, FILTER_REJECT is the same as FILTER_SKIP. RefPtr<Node> provisionalResult = m_candidateNode.node; - bool nodeWasAccepted = acceptNode(state, provisionalResult.get()) == NodeFilter::FILTER_ACCEPT; - if (state && state->hadException()) - break; + bool nodeWasAccepted = acceptNode(provisionalResult.get()) == NodeFilter::FILTER_ACCEPT; if (nodeWasAccepted) { m_referenceNode = m_candidateNode; - result = provisionalResult.release(); + result = WTFMove(provisionalResult); break; } } m_candidateNode.clear(); - return result.release(); + return result; } -void NodeIterator::detach() -{ - root()->document().detachNodeIterator(this); - m_detached = true; - m_referenceNode.node.clear(); -} - -void NodeIterator::nodeWillBeRemoved(Node* removedNode) +void NodeIterator::nodeWillBeRemoved(Node& removedNode) { updateForNodeRemoval(removedNode, m_candidateNode); updateForNodeRemoval(removedNode, m_referenceNode); } -void NodeIterator::updateForNodeRemoval(Node* removedNode, NodePointer& referenceNode) const +void NodeIterator::updateForNodeRemoval(Node& removedNode, NodePointer& referenceNode) const { - ASSERT(!m_detached); - ASSERT(removedNode); - ASSERT(&root()->document() == &removedNode->document()); + ASSERT(&root().document() == &removedNode.document()); // Iterator is not affected if the removed node is the reference node and is the root. // or if removed node is not the reference node, or the ancestor of the reference node. - if (!removedNode->isDescendantOf(root())) + if (!removedNode.isDescendantOf(root())) return; - bool willRemoveReferenceNode = removedNode == referenceNode.node; + bool willRemoveReferenceNode = &removedNode == referenceNode.node; bool willRemoveReferenceNodeAncestor = referenceNode.node && referenceNode.node->isDescendantOf(removedNode); if (!willRemoveReferenceNode && !willRemoveReferenceNodeAncestor) return; if (referenceNode.isPointerBeforeNode) { - Node* node = NodeTraversal::next(removedNode, root()); + Node* node = NodeTraversal::next(removedNode, &root()); if (node) { // Move out from under the node being removed if the new reference // node is a descendant of the node being removed. while (node && node->isDescendantOf(removedNode)) - node = NodeTraversal::next(node, root()); + node = NodeTraversal::next(*node, &root()); if (node) referenceNode.node = node; } else { @@ -188,8 +165,8 @@ void NodeIterator::updateForNodeRemoval(Node* removedNode, NodePointer& referenc // Move out from under the node being removed if the reference node is // a descendant of the node being removed. if (willRemoveReferenceNodeAncestor) { - while (node && node->isDescendantOf(removedNode)) - node = NodeTraversal::previous(node); + while (node && node->isDescendantOf(&removedNode)) + node = NodeTraversal::previous(*node); } if (node) { // Removing last node. @@ -207,18 +184,18 @@ void NodeIterator::updateForNodeRemoval(Node* removedNode, NodePointer& referenc // a descendant of the node being removed. if (willRemoveReferenceNodeAncestor) { while (node && node->isDescendantOf(removedNode)) - node = NodeTraversal::previous(node); + node = NodeTraversal::previous(*node); } if (node) referenceNode.node = node; } else { // FIXME: This branch doesn't appear to have any LayoutTests. - node = NodeTraversal::next(removedNode, root()); + node = NodeTraversal::next(removedNode, &root()); // Move out from under the node being removed if the reference node is // a descendant of the node being removed. if (willRemoveReferenceNodeAncestor) { while (node && node->isDescendantOf(removedNode)) - node = NodeTraversal::previous(node); + node = NodeTraversal::previous(*node); } if (node) referenceNode.node = node; @@ -226,5 +203,4 @@ void NodeIterator::updateForNodeRemoval(Node* removedNode, NodePointer& referenc } } - } // namespace WebCore diff --git a/Source/WebCore/dom/NodeIterator.h b/Source/WebCore/dom/NodeIterator.h index 48925ad20..28bcaf00c 100644 --- a/Source/WebCore/dom/NodeIterator.h +++ b/Source/WebCore/dom/NodeIterator.h @@ -22,62 +22,48 @@ * */ -#ifndef NodeIterator_h -#define NodeIterator_h +#pragma once #include "NodeFilter.h" #include "ScriptWrappable.h" #include "Traversal.h" -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> namespace WebCore { - typedef int ExceptionCode; +class NodeIterator : public ScriptWrappable, public RefCounted<NodeIterator>, public NodeIteratorBase { +public: + static Ref<NodeIterator> create(Node&, unsigned whatToShow, RefPtr<NodeFilter>&&); + WEBCORE_EXPORT ~NodeIterator(); - class NodeIterator : public ScriptWrappable, public RefCounted<NodeIterator>, public NodeIteratorBase { - public: - static PassRefPtr<NodeIterator> create(PassRefPtr<Node> rootNode, unsigned whatToShow, PassRefPtr<NodeFilter> filter, bool expandEntityReferences) - { - return adoptRef(new NodeIterator(rootNode, whatToShow, filter, expandEntityReferences)); - } - ~NodeIterator(); + WEBCORE_EXPORT RefPtr<Node> nextNode(); + WEBCORE_EXPORT RefPtr<Node> previousNode(); + void detach() { } // This is now a no-op as per the DOM specification. - PassRefPtr<Node> nextNode(JSC::ExecState*, ExceptionCode&); - PassRefPtr<Node> previousNode(JSC::ExecState*, ExceptionCode&); - void detach(); + Node* referenceNode() const { return m_referenceNode.node.get(); } + bool pointerBeforeReferenceNode() const { return m_referenceNode.isPointerBeforeNode; } - Node* referenceNode() const { return m_referenceNode.node.get(); } - bool pointerBeforeReferenceNode() const { return m_referenceNode.isPointerBeforeNode; } + // This function is called before any node is removed from the document tree. + void nodeWillBeRemoved(Node&); - // This function is called before any node is removed from the document tree. - void nodeWillBeRemoved(Node*); +private: + NodeIterator(Node&, unsigned whatToShow, RefPtr<NodeFilter>&&); - // Do not call these functions. They are just scaffolding to support the Objective-C bindings. - // They operate in the main thread normal world, and they swallow JS exceptions. - PassRefPtr<Node> nextNode(ExceptionCode& ec) { return nextNode(execStateFromNode(mainThreadNormalWorld(), referenceNode()), ec); } - PassRefPtr<Node> previousNode(ExceptionCode& ec) { return previousNode(execStateFromNode(mainThreadNormalWorld(), referenceNode()), ec); } + struct NodePointer { + RefPtr<Node> node; + bool isPointerBeforeNode { true }; - private: - NodeIterator(PassRefPtr<Node>, unsigned whatToShow, PassRefPtr<NodeFilter>, bool expandEntityReferences); + NodePointer() = default; + NodePointer(Node&, bool); - struct NodePointer { - RefPtr<Node> node; - bool isPointerBeforeNode; - NodePointer(); - NodePointer(PassRefPtr<Node>, bool); - void clear(); - bool moveToNext(Node* root); - bool moveToPrevious(Node* root); - }; + void clear(); + bool moveToNext(Node& root); + bool moveToPrevious(Node& root); + }; - void updateForNodeRemoval(Node* nodeToBeRemoved, NodePointer&) const; + void updateForNodeRemoval(Node& nodeToBeRemoved, NodePointer&) const; - NodePointer m_referenceNode; - NodePointer m_candidateNode; - bool m_detached; - }; + NodePointer m_referenceNode; + NodePointer m_candidateNode; +}; } // namespace WebCore - -#endif // NodeIterator_h diff --git a/Source/WebCore/dom/NodeIterator.idl b/Source/WebCore/dom/NodeIterator.idl index 5cf78dff9..f719b459c 100644 --- a/Source/WebCore/dom/NodeIterator.idl +++ b/Source/WebCore/dom/NodeIterator.idl @@ -18,20 +18,18 @@ * Boston, MA 02110-1301, USA. */ -// Introduced in DOM Level 2: [ - JSCustomMarkFunction, + ExportToWrappedFunction, ImplementationLacksVTable, + JSCustomMarkFunction, ] interface NodeIterator { readonly attribute Node root; readonly attribute unsigned long whatToShow; readonly attribute NodeFilter filter; - readonly attribute boolean expandEntityReferences; readonly attribute Node referenceNode; readonly attribute boolean pointerBeforeReferenceNode; - [CallWith=ScriptState, RaisesException] Node nextNode(); - [CallWith=ScriptState, RaisesException] Node previousNode(); + Node nextNode(); + Node previousNode(); void detach(); }; - diff --git a/Source/WebCore/dom/NodeList.h b/Source/WebCore/dom/NodeList.h index cd82115fd..1b99ce600 100644 --- a/Source/WebCore/dom/NodeList.h +++ b/Source/WebCore/dom/NodeList.h @@ -21,8 +21,7 @@ * */ -#ifndef NodeList_h -#define NodeList_h +#pragma once #include "ScriptWrappable.h" #include <wtf/Forward.h> @@ -39,14 +38,23 @@ public: // DOM methods & attributes for NodeList virtual unsigned length() const = 0; virtual Node* item(unsigned index) const = 0; - virtual Node* namedItem(const AtomicString&) const = 0; + + class Iterator { + public: + explicit Iterator(NodeList& list) : m_list(list) { } + Node* next() { return m_list->item(m_index++); } + + private: + size_t m_index { 0 }; + Ref<NodeList> m_list; + }; + Iterator createIterator() { return Iterator(*this); } // Other methods (not part of DOM) virtual bool isLiveNodeList() const { return false; } virtual bool isChildNodeList() const { return false; } virtual bool isEmptyNodeList() const { return false; } + virtual size_t memoryCost() const { return 0; } }; } // namespace WebCore - -#endif // NodeList_h diff --git a/Source/WebCore/dom/NodeList.idl b/Source/WebCore/dom/NodeList.idl index 2d419bf73..2e7bda3b1 100644 --- a/Source/WebCore/dom/NodeList.idl +++ b/Source/WebCore/dom/NodeList.idl @@ -20,13 +20,13 @@ [ CustomIsReachable, - JSCustomGetOwnPropertySlotAndDescriptor, + CustomToJSObject, + ExportToWrappedFunction, + JSCustomHeader, + ReportExtraMemoryCost, SkipVTableValidation, ] interface NodeList { - getter Node item(unsigned long index); - readonly attribute unsigned long length; - + iterable<Node>; }; - diff --git a/Source/WebCore/dom/NodeRareData.h b/Source/WebCore/dom/NodeRareData.h index a4880f841..4153353ff 100644 --- a/Source/WebCore/dom/NodeRareData.h +++ b/Source/WebCore/dom/NodeRareData.h @@ -19,12 +19,11 @@ * */ -#ifndef NodeRareData_h -#define NodeRareData_h +#pragma once #include "ChildNodeList.h" -#include "ClassNodeList.h" -#include "DOMSettableTokenList.h" +#include "ClassCollection.h" +#include "DOMTokenList.h" #include "HTMLCollection.h" #include "HTMLNames.h" #include "LiveNodeList.h" @@ -32,39 +31,49 @@ #include "MutationObserverRegistration.h" #include "Page.h" #include "QualifiedName.h" -#include "TagNodeList.h" +#include "TagCollection.h" +#include <wtf/HashSet.h> +#include <wtf/text/AtomicString.h> + #if ENABLE(VIDEO_TRACK) #include "TextTrack.h" #endif -#include <wtf/HashSet.h> -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> -#include <wtf/text/AtomicString.h> -#include <wtf/text/StringHash.h> namespace WebCore { class LabelsNodeList; +class NameNodeList; class RadioNodeList; class TreeScope; +template <class ListType> struct NodeListTypeIdentifier; +template <> struct NodeListTypeIdentifier<NameNodeList> { static int value() { return 0; } }; +template <> struct NodeListTypeIdentifier<RadioNodeList> { static int value() { return 1; } }; +template <> struct NodeListTypeIdentifier<LabelsNodeList> { static int value() { return 2; } }; + class NodeListsNodeData { WTF_MAKE_NONCOPYABLE(NodeListsNodeData); WTF_MAKE_FAST_ALLOCATED; public: + NodeListsNodeData() + : m_childNodeList(nullptr) + , m_emptyChildNodeList(nullptr) + { + } + void clearChildNodeListCache() { if (m_childNodeList) m_childNodeList->invalidateCache(); } - PassRefPtr<ChildNodeList> ensureChildNodeList(ContainerNode& node) + Ref<ChildNodeList> ensureChildNodeList(ContainerNode& node) { ASSERT(!m_emptyChildNodeList); if (m_childNodeList) - return m_childNodeList; - RefPtr<ChildNodeList> list = ChildNodeList::create(node); - m_childNodeList = list.get(); - return list.release(); + return *m_childNodeList; + auto list = ChildNodeList::create(node); + m_childNodeList = list.ptr(); + return list; } void removeChildNodeList(ChildNodeList* list) @@ -75,14 +84,14 @@ public: m_childNodeList = nullptr; } - PassRefPtr<EmptyNodeList> ensureEmptyChildNodeList(Node& node) + Ref<EmptyNodeList> ensureEmptyChildNodeList(Node& node) { ASSERT(!m_childNodeList); if (m_emptyChildNodeList) - return m_emptyChildNodeList; - RefPtr<EmptyNodeList> list = EmptyNodeList::create(node); - m_emptyChildNodeList = list.get(); - return list.release(); + return *m_emptyChildNodeList; + auto list = EmptyNodeList::create(node); + m_emptyChildNodeList = list.ptr(); + return list; } void removeEmptyChildNodeList(EmptyNodeList* list) @@ -93,79 +102,65 @@ public: m_emptyChildNodeList = nullptr; } - template <typename StringType> struct NodeListCacheMapEntryHash { - static unsigned hash(const std::pair<unsigned char, StringType>& entry) + static unsigned hash(const std::pair<unsigned char, AtomicString>& entry) { - return DefaultHash<StringType>::Hash::hash(entry.second) + entry.first; + return DefaultHash<AtomicString>::Hash::hash(entry.second) + entry.first; } - static bool equal(const std::pair<unsigned char, StringType>& a, const std::pair<unsigned char, StringType>& b) { return a == b; } - static const bool safeToCompareToEmptyOrDeleted = DefaultHash<StringType>::Hash::safeToCompareToEmptyOrDeleted; + static bool equal(const std::pair<unsigned char, AtomicString>& a, const std::pair<unsigned char, AtomicString>& b) { return a.first == b.first && DefaultHash<AtomicString>::Hash::equal(a.second, b.second); } + static const bool safeToCompareToEmptyOrDeleted = DefaultHash<AtomicString>::Hash::safeToCompareToEmptyOrDeleted; }; - typedef HashMap<std::pair<unsigned char, AtomicString>, LiveNodeList*, NodeListCacheMapEntryHash<AtomicString>> NodeListAtomicNameCacheMap; - typedef HashMap<std::pair<unsigned char, String>, LiveNodeList*, NodeListCacheMapEntryHash<String>> NodeListNameCacheMap; - typedef HashMap<std::pair<unsigned char, AtomicString>, HTMLCollection*, NodeListCacheMapEntryHash<AtomicString>> CollectionCacheMap; - typedef HashMap<QualifiedName, TagNodeList*> TagNodeListCacheNS; + typedef HashMap<std::pair<unsigned char, AtomicString>, LiveNodeList*, NodeListCacheMapEntryHash> NodeListAtomicNameCacheMap; + typedef HashMap<std::pair<unsigned char, AtomicString>, HTMLCollection*, NodeListCacheMapEntryHash> CollectionCacheMap; + typedef HashMap<QualifiedName, TagCollectionNS*> TagCollectionNSCache; template<typename T, typename ContainerType> - PassRefPtr<T> addCacheWithAtomicName(ContainerType& container, LiveNodeList::Type type, const AtomicString& name) + ALWAYS_INLINE Ref<T> addCacheWithAtomicName(ContainerType& container, const AtomicString& name) { - NodeListAtomicNameCacheMap::AddResult result = m_atomicNameCaches.add(namedNodeListKey(type, name), nullptr); + NodeListAtomicNameCacheMap::AddResult result = m_atomicNameCaches.fastAdd(namedNodeListKey<T>(name), nullptr); if (!result.isNewEntry) - return static_cast<T*>(result.iterator->value); + return static_cast<T&>(*result.iterator->value); - RefPtr<T> list = T::create(container, type, name); - result.iterator->value = list.get(); - return list.release(); + auto list = T::create(container, name); + result.iterator->value = &list.get(); + return list; } - template<typename T> - PassRefPtr<T> addCacheWithName(ContainerNode& node, LiveNodeList::Type type, const String& name) - { - NodeListNameCacheMap::AddResult result = m_nameCaches.add(namedNodeListKey(type, name), nullptr); - if (!result.isNewEntry) - return static_cast<T*>(result.iterator->value); - - RefPtr<T> list = T::create(node, name); - result.iterator->value = list.get(); - return list.release(); - } - - PassRefPtr<TagNodeList> addCacheWithQualifiedName(ContainerNode& node, const AtomicString& namespaceURI, const AtomicString& localName) + ALWAYS_INLINE Ref<TagCollectionNS> addCachedTagCollectionNS(ContainerNode& node, const AtomicString& namespaceURI, const AtomicString& localName) { QualifiedName name(nullAtom, localName, namespaceURI); - TagNodeListCacheNS::AddResult result = m_tagNodeListCacheNS.add(name, nullptr); + TagCollectionNSCache::AddResult result = m_tagCollectionNSCache.fastAdd(name, nullptr); if (!result.isNewEntry) - return result.iterator->value; + return *result.iterator->value; - RefPtr<TagNodeList> list = TagNodeList::create(node, namespaceURI, localName); - result.iterator->value = list.get(); - return list.release(); + auto list = TagCollectionNS::create(node, namespaceURI, localName); + result.iterator->value = list.ptr(); + return list; } template<typename T, typename ContainerType> - PassRefPtr<T> addCachedCollection(ContainerType& container, CollectionType collectionType, const AtomicString& name) + ALWAYS_INLINE Ref<T> addCachedCollection(ContainerType& container, CollectionType collectionType, const AtomicString& name) { - CollectionCacheMap::AddResult result = m_cachedCollections.add(namedCollectionKey(collectionType, name), nullptr); + CollectionCacheMap::AddResult result = m_cachedCollections.fastAdd(namedCollectionKey(collectionType, name), nullptr); if (!result.isNewEntry) - return static_cast<T*>(result.iterator->value); + return static_cast<T&>(*result.iterator->value); - RefPtr<T> list = T::create(container, collectionType, name); - result.iterator->value = list.get(); - return list.release(); + auto list = T::create(container, collectionType, name); + result.iterator->value = &list.get(); + return list; } template<typename T, typename ContainerType> - PassRefPtr<T> addCachedCollection(ContainerType& container, CollectionType collectionType) + ALWAYS_INLINE Ref<T> addCachedCollection(ContainerType& container, CollectionType collectionType) { - CollectionCacheMap::AddResult result = m_cachedCollections.add(namedCollectionKey(collectionType, starAtom), nullptr); + CollectionCacheMap::AddResult result = m_cachedCollections.fastAdd(namedCollectionKey(collectionType, starAtom), nullptr); if (!result.isNewEntry) - return static_cast<T*>(result.iterator->value); + return static_cast<T&>(*result.iterator->value); - RefPtr<T> list = T::create(container, collectionType); - result.iterator->value = list.get(); - return list.release(); + auto list = T::create(container, collectionType); + result.iterator->value = &list.get(); + return list; } template<typename T> @@ -174,29 +169,22 @@ public: return static_cast<T*>(m_cachedCollections.get(namedCollectionKey(collectionType, starAtom))); } - void removeCacheWithAtomicName(LiveNodeList* list, const AtomicString& name = starAtom) - { - ASSERT(list == m_atomicNameCaches.get(namedNodeListKey(list->type(), name))); - if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode())) - return; - m_atomicNameCaches.remove(namedNodeListKey(list->type(), name)); - } - - void removeCacheWithName(LiveNodeList* list, const String& name) + template <class NodeListType> + void removeCacheWithAtomicName(NodeListType* list, const AtomicString& name = starAtom) { - ASSERT(list == m_nameCaches.get(namedNodeListKey(list->type(), name))); + ASSERT(list == m_atomicNameCaches.get(namedNodeListKey<NodeListType>(name))); if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode())) return; - m_nameCaches.remove(namedNodeListKey(list->type(), name)); + m_atomicNameCaches.remove(namedNodeListKey<NodeListType>(name)); } - void removeCacheWithQualifiedName(LiveNodeList* list, const AtomicString& namespaceURI, const AtomicString& localName) + void removeCachedTagCollectionNS(HTMLCollection& collection, const AtomicString& namespaceURI, const AtomicString& localName) { QualifiedName name(nullAtom, localName, namespaceURI); - ASSERT(list == m_tagNodeListCacheNS.get(name)); - if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode())) + ASSERT(&collection == m_tagCollectionNSCache.get(name)); + if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(collection.ownerNode())) return; - m_tagNodeListCacheNS.remove(name); + m_tagCollectionNSCache.remove(name); } void removeCachedCollection(HTMLCollection* collection, const AtomicString& name = starAtom) @@ -207,68 +195,42 @@ public: m_cachedCollections.remove(namedCollectionKey(collection->type(), name)); } - static PassOwnPtr<NodeListsNodeData> create() - { - return adoptPtr(new NodeListsNodeData); - } - - void invalidateCaches(const QualifiedName* attrName = 0); - bool isEmpty() const - { - return m_atomicNameCaches.isEmpty() && m_nameCaches.isEmpty() && m_cachedCollections.isEmpty() && m_tagNodeListCacheNS.isEmpty(); - } + void invalidateCaches(const QualifiedName* attrName = nullptr); void adoptTreeScope() { invalidateCaches(); } - void adoptDocument(Document* oldDocument, Document* newDocument) + void adoptDocument(Document& oldDocument, Document& newDocument) { - invalidateCaches(); + if (&oldDocument == &newDocument) { + invalidateCaches(); + return; + } + + for (auto& cache : m_atomicNameCaches.values()) + cache->invalidateCache(oldDocument); - if (oldDocument != newDocument) { - for (auto it = m_atomicNameCaches.begin(), end = m_atomicNameCaches.end(); it != end; ++it) { - LiveNodeList& list = *it->value; - oldDocument->unregisterNodeList(list); - newDocument->registerNodeList(list); - } - - for (auto it = m_nameCaches.begin(), end = m_nameCaches.end(); it != end; ++it) { - LiveNodeList& list = *it->value; - oldDocument->unregisterNodeList(list); - newDocument->registerNodeList(list); - } - - for (auto it = m_tagNodeListCacheNS.begin(), end = m_tagNodeListCacheNS.end(); it != end; ++it) { - LiveNodeList& list = *it->value; - ASSERT(!list.isRootedAtDocument()); - oldDocument->unregisterNodeList(list); - newDocument->registerNodeList(list); - } - - for (auto it = m_cachedCollections.begin(), end = m_cachedCollections.end(); it != end; ++it) { - HTMLCollection& collection = *it->value; - oldDocument->unregisterCollection(collection); - newDocument->registerCollection(collection); - } + for (auto& list : m_tagCollectionNSCache.values()) { + ASSERT(!list->isRootedAtDocument()); + list->invalidateCache(oldDocument); } + + for (auto& collection : m_cachedCollections.values()) + collection->invalidateCache(oldDocument); } private: - NodeListsNodeData() - : m_childNodeList(nullptr) - , m_emptyChildNodeList(nullptr) - { } - std::pair<unsigned char, AtomicString> namedCollectionKey(CollectionType type, const AtomicString& name) { return std::pair<unsigned char, AtomicString>(type, name); } - std::pair<unsigned char, String> namedNodeListKey(LiveNodeList::Type type, const String& name) + template <class NodeListType> + std::pair<unsigned char, AtomicString> namedNodeListKey(const AtomicString& name) { - return std::pair<unsigned char, String>(type, name); + return std::pair<unsigned char, AtomicString>(NodeListTypeIdentifier<NodeListType>::value(), name); } bool deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(Node&); @@ -278,34 +240,33 @@ private: EmptyNodeList* m_emptyChildNodeList; NodeListAtomicNameCacheMap m_atomicNameCaches; - NodeListNameCacheMap m_nameCaches; - TagNodeListCacheNS m_tagNodeListCacheNS; + TagCollectionNSCache m_tagCollectionNSCache; CollectionCacheMap m_cachedCollections; }; class NodeMutationObserverData { WTF_MAKE_NONCOPYABLE(NodeMutationObserverData); WTF_MAKE_FAST_ALLOCATED; public: - Vector<OwnPtr<MutationObserverRegistration>> registry; + Vector<std::unique_ptr<MutationObserverRegistration>> registry; HashSet<MutationObserverRegistration*> transientRegistry; - static PassOwnPtr<NodeMutationObserverData> create() { return adoptPtr(new NodeMutationObserverData); } - -private: NodeMutationObserverData() { } }; class NodeRareData : public NodeRareDataBase { WTF_MAKE_NONCOPYABLE(NodeRareData); WTF_MAKE_FAST_ALLOCATED; public: - static PassOwnPtr<NodeRareData> create(RenderObject* renderer) { return adoptPtr(new NodeRareData(renderer)); } + NodeRareData(RenderObject* renderer) + : NodeRareDataBase(renderer) + , m_connectedFrameCount(0) + { } - void clearNodeLists() { m_nodeLists.clear(); } + void clearNodeLists() { m_nodeLists = nullptr; } NodeListsNodeData* nodeLists() const { return m_nodeLists.get(); } NodeListsNodeData& ensureNodeLists() { if (!m_nodeLists) - m_nodeLists = NodeListsNodeData::create(); + m_nodeLists = std::make_unique<NodeListsNodeData>(); return *m_nodeLists; } @@ -313,7 +274,7 @@ public: NodeMutationObserverData& ensureMutationObserverData() { if (!m_mutationObserverData) - m_mutationObserverData = NodeMutationObserverData::create(); + m_mutationObserverData = std::make_unique<NodeMutationObserverData>(); return *m_mutationObserverData; } @@ -329,32 +290,37 @@ public: m_connectedFrameCount -= amount; } -protected: - NodeRareData(RenderObject* renderer) - : NodeRareDataBase(renderer) - , m_connectedFrameCount(0) - { } - private: unsigned m_connectedFrameCount : 10; // Must fit Page::maxNumberOfFrames. - OwnPtr<NodeListsNodeData> m_nodeLists; - OwnPtr<NodeMutationObserverData> m_mutationObserverData; + std::unique_ptr<NodeListsNodeData> m_nodeLists; + std::unique_ptr<NodeMutationObserverData> m_mutationObserverData; }; inline bool NodeListsNodeData::deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(Node& ownerNode) { ASSERT(ownerNode.nodeLists() == this); - if ((m_childNodeList ? 1 : 0) + (m_emptyChildNodeList ? 1 : 0) + m_atomicNameCaches.size() + m_nameCaches.size() - + m_tagNodeListCacheNS.size() + m_cachedCollections.size() != 1) + if ((m_childNodeList ? 1 : 0) + (m_emptyChildNodeList ? 1 : 0) + m_atomicNameCaches.size() + + m_tagCollectionNSCache.size() + m_cachedCollections.size() != 1) return false; ownerNode.clearNodeLists(); return true; } +inline NodeRareData* Node::rareData() const +{ + ASSERT_WITH_SECURITY_IMPLICATION(hasRareData()); + return static_cast<NodeRareData*>(m_data.m_rareData); +} + +inline NodeRareData& Node::ensureRareData() +{ + if (!hasRareData()) + materializeRareData(); + return *rareData(); +} + // Ensure the 10 bits reserved for the m_connectedFrameCount cannot overflow -COMPILE_ASSERT(Page::maxNumberOfFrames < 1024, Frame_limit_should_fit_in_rare_data_count); +static_assert(Page::maxNumberOfFrames < 1024, "Frame limit should fit in rare data count"); } // namespace WebCore - -#endif // NodeRareData_h diff --git a/Source/WebCore/dom/NodeRenderStyle.h b/Source/WebCore/dom/NodeRenderStyle.h index a84565f42..246e8db9b 100644 --- a/Source/WebCore/dom/NodeRenderStyle.h +++ b/Source/WebCore/dom/NodeRenderStyle.h @@ -22,8 +22,7 @@ * */ -#ifndef NodeRenderStyle_h -#define NodeRenderStyle_h +#pragma once #include "RenderObject.h" #include "RenderStyle.h" @@ -31,14 +30,12 @@ namespace WebCore { -inline RenderStyle* Node::renderStyle() const +inline const RenderStyle* Node::renderStyle() const { - // Using a ternary here confuses the Solaris Studio 12/12.1/12.2 compilers: - // Bug is CR 6569194, "Problem with question operator binding in inline function" - if (RenderObject* renderer = this->renderer()) - return &renderer->style(); - return nonRendererStyle(); + auto* renderer = this->renderer(); + if (!renderer) + return nullptr; + return &renderer->style(); } -} -#endif +} // namespace WebCore diff --git a/Source/WebCore/dom/NodeRenderingTraversal.cpp b/Source/WebCore/dom/NodeRenderingTraversal.cpp deleted file mode 100644 index d94d7be73..000000000 --- a/Source/WebCore/dom/NodeRenderingTraversal.cpp +++ /dev/null @@ -1,310 +0,0 @@ -/* - * Copyright (C) 2012 Google Inc. All rights reserved. - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "NodeRenderingTraversal.h" - -#include "InsertionPoint.h" -#include "PseudoElement.h" -#include "ShadowRoot.h" - -namespace WebCore { - -namespace NodeRenderingTraversal { - -static Node* findFirstSiblingEnteringInsertionPoints(const Node*); -static Node* findFirstEnteringInsertionPoints(const Node*); -static Node* findFirstFromDistributedNode(const Node*, const InsertionPoint*); -static Node* findLastSiblingEnteringInsertionPoints(const Node*); -static Node* findLastEnteringInsertionPoints(const Node*); -static Node* findLastFromDistributedNode(const Node*, const InsertionPoint*); - -static inline bool nodeCanBeDistributed(const Node* node) -{ - ASSERT(node); - Node* parent = parentNodeForDistribution(node); - if (!parent) - return false; - - if (parent->isShadowRoot()) - return false; - - if (parent->isElementNode() && toElement(parent)->shadowRoot()) - return true; - - return false; -} - -static Node* findFirstSiblingEnteringInsertionPoints(const Node* node) -{ - for (const Node* sibling = node; sibling; sibling = sibling->nextSibling()) { - if (Node* found = findFirstEnteringInsertionPoints(sibling)) - return found; - } - return nullptr; -} - -static Node* findFirstEnteringInsertionPoints(const Node* node) -{ - ASSERT(node); - if (!isActiveInsertionPoint(node)) - return const_cast<Node*>(node); - const InsertionPoint* insertionPoint = toInsertionPoint(node); - if (Node* found = findFirstFromDistributedNode(insertionPoint->firstDistributed(), insertionPoint)) - return found; - return findFirstSiblingEnteringInsertionPoints(node->firstChild()); -} - -static Node* findFirstFromDistributedNode(const Node* node, const InsertionPoint* insertionPoint) -{ - for (const Node* next = node; next; next = insertionPoint->nextDistributedTo(next)) { - if (Node* found = findFirstEnteringInsertionPoints(next)) - return found; - } - return nullptr; -} - -static Node* findLastSiblingEnteringInsertionPoints(const Node* node) -{ - for (const Node* sibling = node; sibling; sibling = sibling->previousSibling()) { - if (Node* found = findLastEnteringInsertionPoints(sibling)) - return found; - } - return nullptr; -} - -static Node* findLastEnteringInsertionPoints(const Node* node) -{ - ASSERT(node); - if (!isActiveInsertionPoint(node)) - return const_cast<Node*>(node); - const InsertionPoint* insertionPoint = toInsertionPoint(node); - if (Node* found = findLastFromDistributedNode(insertionPoint->lastDistributed(), insertionPoint)) - return found; - return findLastSiblingEnteringInsertionPoints(node->lastChild()); -} - -static Node* findLastFromDistributedNode(const Node* node, const InsertionPoint* insertionPoint) -{ - for (const Node* next = node; next; next = insertionPoint->previousDistributedTo(next)) { - if (Node* found = findLastEnteringInsertionPoints(next)) - return found; - } - return nullptr; -} - -enum ShadowRootCrossing { CrossShadowRoot, DontCrossShadowRoot }; - -static ContainerNode* traverseParent(const Node* node, ShadowRootCrossing shadowRootCrossing) -{ - if (node->isPseudoElement()) - return toPseudoElement(node)->hostElement(); - - if (shadowRootCrossing == DontCrossShadowRoot && node->isShadowRoot()) - return 0; - - if (nodeCanBeDistributed(node)) { - if (InsertionPoint* insertionPoint = findInsertionPointOf(node)) - return traverseParent(insertionPoint, shadowRootCrossing); - return nullptr; - } - ContainerNode* parent = node->parentNode(); - if (!parent) - return nullptr; - - if (parent->isShadowRoot()) - return shadowRootCrossing == CrossShadowRoot ? toShadowRoot(parent)->hostElement() : parent; - - if (parent->isInsertionPoint()) { - const InsertionPoint* insertionPoint = toInsertionPoint(parent); - if (insertionPoint->hasDistribution()) - return nullptr; - if (insertionPoint->isActive()) - return traverseParent(parent, shadowRootCrossing); - } - return parent; -} - -static Node* traverseFirstChild(const Node* node, ShadowRootCrossing shadowRootCrossing) -{ - ASSERT(node); - if (node->shadowRoot()) { - if (shadowRootCrossing == DontCrossShadowRoot) - return nullptr; - node = node->shadowRoot(); - } - return findFirstSiblingEnteringInsertionPoints(node->firstChild()); -} - -static Node* traverseLastChild(const Node* node, ShadowRootCrossing shadowRootCrossing) -{ - ASSERT(node); - if (node->shadowRoot()) { - if (shadowRootCrossing == DontCrossShadowRoot) - return nullptr; - node = node->shadowRoot(); - } - return findLastSiblingEnteringInsertionPoints(node->lastChild()); -} - -static Node* traverseNextSibling(const Node* node) -{ - ASSERT(node); - - InsertionPoint* insertionPoint; - if (nodeCanBeDistributed(node) && (insertionPoint = findInsertionPointOf(node))) { - Node* found = findFirstFromDistributedNode(insertionPoint->nextDistributedTo(node), insertionPoint); - if (found) - return found; - return traverseNextSibling(insertionPoint); - } - - for (const Node* sibling = node->nextSibling(); sibling; sibling = sibling->nextSibling()) { - if (Node* found = findFirstEnteringInsertionPoints(sibling)) - return found; - } - if (node->parentNode() && isActiveInsertionPoint(node->parentNode())) - return traverseNextSibling(node->parentNode()); - - return nullptr; -} - -static Node* traversePreviousSibling(const Node* node) -{ - ASSERT(node); - - InsertionPoint* insertionPoint; - if (nodeCanBeDistributed(node) && (insertionPoint = findInsertionPointOf(node))) { - Node* found = findLastFromDistributedNode(insertionPoint->previousDistributedTo(node), insertionPoint); - if (found) - return found; - return traversePreviousSibling(insertionPoint); - } - - for (const Node* sibling = node->previousSibling(); sibling; sibling = sibling->previousSibling()) { - if (Node* found = findLastEnteringInsertionPoints(sibling)) - return found; - } - if (node->parentNode() && isActiveInsertionPoint(node->parentNode())) - return traversePreviousSibling(node->parentNode()); - - return nullptr; -} - -ContainerNode* parentSlow(const Node* node) -{ - ASSERT(!node->isShadowRoot()); - - return traverseParent(node, CrossShadowRoot); -} - -Node* nextSiblingSlow(const Node* node) -{ - ASSERT(!node->isShadowRoot()); - - // FIXME: Why do these functions deal with before/after when other code here doesn't? - Node* nextSibling = 0; - if (node->isBeforePseudoElement()) { - nextSibling = traverseParent(node, CrossShadowRoot); - nextSibling = traverseFirstChild(nextSibling, CrossShadowRoot); - } else - nextSibling = traverseNextSibling(node); - - if (nextSibling || node->isAfterPseudoElement()) - return nextSibling; - - Node* parent = traverseParent(node, CrossShadowRoot); - if (parent && parent->isElementNode()) - return toElement(parent)->afterPseudoElement(); - - return 0; -} - -Node* previousSiblingSlow(const Node* node) -{ - ASSERT(!node->isShadowRoot()); - - Node* previousSibling = 0; - if (node->isAfterPseudoElement()) { - ContainerNode* parent = traverseParent(node, CrossShadowRoot); - previousSibling = traverseLastChild(parent, CrossShadowRoot); - } else - previousSibling = traversePreviousSibling(node); - - if (previousSibling || node->isBeforePseudoElement()) - return previousSibling; - - ContainerNode* parent = traverseParent(node, CrossShadowRoot); - if (parent && parent->isElementNode()) - return toElement(parent)->beforePseudoElement(); - - return 0; -} - -Node* nextInScope(const Node* node) -{ - ASSERT(!isActiveInsertionPoint(node)); - - if (Node* next = traverseFirstChild(node, DontCrossShadowRoot)) - return next; - if (Node* next = traverseNextSibling(node)) - return next; - const Node* current = node; - while (current && !traverseNextSibling(current)) - current = traverseParent(current, DontCrossShadowRoot); - return current ? traverseNextSibling(current) : 0; -} - -Node* previousInScope(const Node* node) -{ - ASSERT(!isActiveInsertionPoint(node)); - - if (Node* current = traversePreviousSibling(node)) { - while (Node* child = traverseLastChild(current, DontCrossShadowRoot)) - current = child; - return current; - } - return traverseParent(node, DontCrossShadowRoot); -} - -Node* parentInScope(const Node* node) -{ - ASSERT(!isActiveInsertionPoint(node)); - - return traverseParent(node, DontCrossShadowRoot); -} - -Node* lastChildInScope(const Node* node) -{ - ASSERT(!isActiveInsertionPoint(node)); - - return traverseLastChild(node, DontCrossShadowRoot); -} - -} - -} // namespace diff --git a/Source/WebCore/dom/NodeRenderingTraversal.h b/Source/WebCore/dom/NodeRenderingTraversal.h deleted file mode 100644 index e8216a0f3..000000000 --- a/Source/WebCore/dom/NodeRenderingTraversal.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2012 Google Inc. All rights reserved. - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef NodeRenderingTraversal_h -#define NodeRenderingTraversal_h - -#include "ContainerNode.h" - -namespace WebCore { - -namespace NodeRenderingTraversal { - -ContainerNode* parent(const Node*); -Node* nextSibling(const Node*); -Node* previousSibling(const Node*); - -Node* nextInScope(const Node*); -Node* previousInScope(const Node*); -Node* parentInScope(const Node*); -Node* lastChildInScope(const Node*); - -ContainerNode* parentSlow(const Node*); -Node* nextSiblingSlow(const Node*); -Node* previousSiblingSlow(const Node*); - -inline ContainerNode* parent(const Node* node) -{ - if (node->needsNodeRenderingTraversalSlowPath()) - return parentSlow(node); - - ASSERT(node->parentNode() == parentSlow(node)); - return node->parentNodeGuaranteedHostFree(); -} - -inline Node* nextSibling(const Node* node) -{ - if (node->needsNodeRenderingTraversalSlowPath()) - return nextSiblingSlow(node); - - ASSERT(nextSiblingSlow(node) == node->nextSibling()); - return node->nextSibling(); -} - -inline Node* previousSibling(const Node* node) -{ - if (node->needsNodeRenderingTraversalSlowPath()) - return previousSiblingSlow(node); - - ASSERT(previousSiblingSlow(node) == node->previousSibling()); - return node->previousSibling(); -} - -} - -} // namespace WebCore - -#endif diff --git a/Source/WebCore/dom/NodeTraversal.cpp b/Source/WebCore/dom/NodeTraversal.cpp index 7ad11307c..8301e4a36 100644 --- a/Source/WebCore/dom/NodeTraversal.cpp +++ b/Source/WebCore/dom/NodeTraversal.cpp @@ -31,81 +31,81 @@ namespace WebCore { namespace NodeTraversal { -Node* previousIncludingPseudo(const Node* current, const Node* stayWithin) +Node* previousIncludingPseudo(const Node& current, const Node* stayWithin) { Node* previous; - if (current == stayWithin) - return 0; - if ((previous = current->pseudoAwarePreviousSibling())) { + if (¤t == stayWithin) + return nullptr; + if ((previous = current.pseudoAwarePreviousSibling())) { while (previous->pseudoAwareLastChild()) previous = previous->pseudoAwareLastChild(); return previous; } - return current->isPseudoElement() ? toPseudoElement(current)->hostElement() : current->parentNode(); + return is<PseudoElement>(current) ? downcast<PseudoElement>(current).hostElement() : current.parentNode(); } -Node* nextIncludingPseudo(const Node* current, const Node* stayWithin) +Node* nextIncludingPseudo(const Node& current, const Node* stayWithin) { Node* next; - if ((next = current->pseudoAwareFirstChild())) + if ((next = current.pseudoAwareFirstChild())) return next; - if (current == stayWithin) - return 0; - if ((next = current->pseudoAwareNextSibling())) + if (¤t == stayWithin) + return nullptr; + if ((next = current.pseudoAwareNextSibling())) return next; - current = current->isPseudoElement() ? toPseudoElement(current)->hostElement() : current->parentNode(); - for (; current; current = current->parentNode()) { - if (current == stayWithin) - return 0; - if ((next = current->pseudoAwareNextSibling())) + const Node* ancestor = is<PseudoElement>(current) ? downcast<PseudoElement>(current).hostElement() : current.parentNode(); + for (; ancestor; ancestor = ancestor->parentNode()) { + if (ancestor == stayWithin) + return nullptr; + if ((next = ancestor->pseudoAwareNextSibling())) return next; } - return 0; + return nullptr; } -Node* nextIncludingPseudoSkippingChildren(const Node* current, const Node* stayWithin) +Node* nextIncludingPseudoSkippingChildren(const Node& current, const Node* stayWithin) { Node* next; - if (current == stayWithin) - return 0; - if ((next = current->pseudoAwareNextSibling())) + if (¤t == stayWithin) + return nullptr; + if ((next = current.pseudoAwareNextSibling())) return next; - current = current->isPseudoElement() ? toPseudoElement(current)->hostElement() : current->parentNode(); - for (; current; current = current->parentNode()) { - if (current == stayWithin) - return 0; - if ((next = current->pseudoAwareNextSibling())) + const Node* ancestor = is<PseudoElement>(current) ? downcast<PseudoElement>(current).hostElement() : current.parentNode(); + for (; ancestor; ancestor = ancestor->parentNode()) { + if (ancestor == stayWithin) + return nullptr; + if ((next = ancestor->pseudoAwareNextSibling())) return next; } - return 0; + return nullptr; } -Node* nextAncestorSibling(const Node* current) +Node* nextAncestorSibling(const Node& current) { - ASSERT(!current->nextSibling()); - for (current = current->parentNode(); current; current = current->parentNode()) { - if (current->nextSibling()) - return current->nextSibling(); + ASSERT(!current.nextSibling()); + for (auto* ancestor = current.parentNode(); ancestor; ancestor = ancestor->parentNode()) { + if (ancestor->nextSibling()) + return ancestor->nextSibling(); } - return 0; + return nullptr; } -Node* nextAncestorSibling(const Node* current, const Node* stayWithin) +Node* nextAncestorSibling(const Node& current, const Node* stayWithin) { - ASSERT(!current->nextSibling()); - ASSERT(current != stayWithin); - for (current = current->parentNode(); current; current = current->parentNode()) { - if (current == stayWithin) - return 0; - if (current->nextSibling()) - return current->nextSibling(); + ASSERT(!current.nextSibling()); + ASSERT(¤t != stayWithin); + for (auto* ancestor = current.parentNode(); ancestor; ancestor = ancestor->parentNode()) { + if (ancestor == stayWithin) + return nullptr; + if (ancestor->nextSibling()) + return ancestor->nextSibling(); } - return 0; + return nullptr; } -Node* last(const ContainerNode* current) +Node* last(const ContainerNode& current) { - Node* node = current->lastChild(); + Node* node = current.lastChild(); if (!node) return nullptr; while (node->lastChild()) @@ -113,69 +113,70 @@ Node* last(const ContainerNode* current) return node; } -Node* deepLastChild(Node* node) +Node* deepLastChild(Node& node) { - while (node->lastChild()) - node = node->lastChild(); - return node; + Node* lastChild = &node; + while (lastChild->lastChild()) + lastChild = lastChild->lastChild(); + return lastChild; } -Node* previousSkippingChildren(const Node* current, const Node* stayWithin) +Node* previousSkippingChildren(const Node& current, const Node* stayWithin) { - if (current == stayWithin) - return 0; - if (current->previousSibling()) - return current->previousSibling(); - for (current = current->parentNode(); current; current = current->parentNode()) { - if (current == stayWithin) - return 0; - if (current->previousSibling()) - return current->previousSibling(); + if (¤t == stayWithin) + return nullptr; + if (current.previousSibling()) + return current.previousSibling(); + for (auto* ancestor = current.parentNode(); ancestor; ancestor = ancestor->parentNode()) { + if (ancestor == stayWithin) + return nullptr; + if (ancestor->previousSibling()) + return ancestor->previousSibling(); } - return 0; + return nullptr; } -Node* nextPostOrder(const Node* current, const Node* stayWithin) +Node* nextPostOrder(const Node& current, const Node* stayWithin) { - if (current == stayWithin) - return 0; - if (!current->nextSibling()) - return current->parentNode(); - Node* next = current->nextSibling(); + if (¤t == stayWithin) + return nullptr; + if (!current.nextSibling()) + return current.parentNode(); + Node* next = current.nextSibling(); while (next->firstChild()) next = next->firstChild(); return next; } -static Node* previousAncestorSiblingPostOrder(const Node* current, const Node* stayWithin) +static Node* previousAncestorSiblingPostOrder(const Node& current, const Node* stayWithin) { - ASSERT(!current->previousSibling()); - for (current = current->parentNode(); current; current = current->parentNode()) { - if (current == stayWithin) - return 0; - if (current->previousSibling()) - return current->previousSibling(); + ASSERT(!current.previousSibling()); + for (auto* ancestor = current.parentNode(); ancestor; ancestor = ancestor->parentNode()) { + if (ancestor == stayWithin) + return nullptr; + if (ancestor->previousSibling()) + return ancestor->previousSibling(); } - return 0; + return nullptr; } -Node* previousPostOrder(const Node* current, const Node* stayWithin) +Node* previousPostOrder(const Node& current, const Node* stayWithin) { - if (current->lastChild()) - return current->lastChild(); - if (current == stayWithin) - return 0; - if (current->previousSibling()) - return current->previousSibling(); + if (current.lastChild()) + return current.lastChild(); + if (¤t == stayWithin) + return nullptr; + if (current.previousSibling()) + return current.previousSibling(); return previousAncestorSiblingPostOrder(current, stayWithin); } -Node* previousSkippingChildrenPostOrder(const Node* current, const Node* stayWithin) +Node* previousSkippingChildrenPostOrder(const Node& current, const Node* stayWithin) { - if (current == stayWithin) - return 0; - if (current->previousSibling()) - return current->previousSibling(); + if (¤t == stayWithin) + return nullptr; + if (current.previousSibling()) + return current.previousSibling(); return previousAncestorSiblingPostOrder(current, stayWithin); } diff --git a/Source/WebCore/dom/NodeTraversal.h b/Source/WebCore/dom/NodeTraversal.h index 41870646b..8ab9846dc 100644 --- a/Source/WebCore/dom/NodeTraversal.h +++ b/Source/WebCore/dom/NodeTraversal.h @@ -22,8 +22,7 @@ * */ -#ifndef NodeTraversal_h -#define NodeTraversal_h +#pragma once #include "ContainerNode.h" #include "Text.h" @@ -35,107 +34,97 @@ namespace NodeTraversal { // This uses the same order that tags appear in the source file. If the stayWithin // argument is non-null, the traversal will stop once the specified node is reached. // This can be used to restrict traversal to a particular sub-tree. -Node* next(const Node*); -Node* next(const Node*, const Node* stayWithin); -Node* next(const ContainerNode*); -Node* next(const ContainerNode*, const Node* stayWithin); -Node* next(const Text*); -Node* next(const Text*, const Node* stayWithin); +Node* next(const Node&); +Node* next(const Node&, const Node* stayWithin); +Node* next(const ContainerNode&); +Node* next(const ContainerNode&, const Node* stayWithin); +Node* next(const Text&); +Node* next(const Text&, const Node* stayWithin); // Like next, but skips children and starts with the next sibling. -Node* nextSkippingChildren(const Node*); -Node* nextSkippingChildren(const Node*, const Node* stayWithin); -Node* nextSkippingChildren(const ContainerNode*); -Node* nextSkippingChildren(const ContainerNode*, const Node* stayWithin); +Node* nextSkippingChildren(const Node&); +Node* nextSkippingChildren(const Node&, const Node* stayWithin); // Does a reverse pre-order traversal to find the node that comes before the current one in document order -Node* last(const ContainerNode*); -Node* previous(const Node*, const Node* stayWithin = 0); +WEBCORE_EXPORT Node* last(const ContainerNode&); +Node* previous(const Node&, const Node* stayWithin = nullptr); // Like previous, but skips children and starts with the next sibling. -Node* previousSkippingChildren(const Node*, const Node* stayWithin = 0); +Node* previousSkippingChildren(const Node&, const Node* stayWithin = nullptr); // Like next, but visits parents after their children. -Node* nextPostOrder(const Node*, const Node* stayWithin = 0); +Node* nextPostOrder(const Node&, const Node* stayWithin = nullptr); // Like previous/previousSkippingChildren, but visits parents before their children. -Node* previousPostOrder(const Node*, const Node* stayWithin = 0); -Node* previousSkippingChildrenPostOrder(const Node*, const Node* stayWithin = 0); +Node* previousPostOrder(const Node&, const Node* stayWithin = nullptr); +Node* previousSkippingChildrenPostOrder(const Node&, const Node* stayWithin = nullptr); // Pre-order traversal including the pseudo-elements. -Node* previousIncludingPseudo(const Node*, const Node* = 0); -Node* nextIncludingPseudo(const Node*, const Node* = 0); -Node* nextIncludingPseudoSkippingChildren(const Node*, const Node* = 0); +Node* previousIncludingPseudo(const Node&, const Node* = nullptr); +Node* nextIncludingPseudo(const Node&, const Node* = nullptr); +Node* nextIncludingPseudoSkippingChildren(const Node&, const Node* = nullptr); } namespace NodeTraversal { -Node* nextAncestorSibling(const Node*); -Node* nextAncestorSibling(const Node*, const Node* stayWithin); -Node* deepLastChild(Node*); +WEBCORE_EXPORT Node* nextAncestorSibling(const Node&); +WEBCORE_EXPORT Node* nextAncestorSibling(const Node&, const Node* stayWithin); +WEBCORE_EXPORT Node* deepLastChild(Node&); template <class NodeType> -inline Node* traverseNextTemplate(NodeType* current) +inline Node* traverseNextTemplate(NodeType& current) { - if (current->firstChild()) - return current->firstChild(); - if (current->nextSibling()) - return current->nextSibling(); + if (current.firstChild()) + return current.firstChild(); + if (current.nextSibling()) + return current.nextSibling(); return nextAncestorSibling(current); } -inline Node* next(const Node* current) { return traverseNextTemplate(current); } -inline Node* next(const ContainerNode* current) { return traverseNextTemplate(current); } +inline Node* next(const Node& current) { return traverseNextTemplate(current); } +inline Node* next(const ContainerNode& current) { return traverseNextTemplate(current); } template <class NodeType> -inline Node* traverseNextTemplate(NodeType* current, const Node* stayWithin) +inline Node* traverseNextTemplate(NodeType& current, const Node* stayWithin) { - if (current->firstChild()) - return current->firstChild(); - if (current == stayWithin) - return 0; - if (current->nextSibling()) - return current->nextSibling(); + if (current.firstChild()) + return current.firstChild(); + if (¤t == stayWithin) + return nullptr; + if (current.nextSibling()) + return current.nextSibling(); return nextAncestorSibling(current, stayWithin); } -inline Node* next(const Node* current, const Node* stayWithin) { return traverseNextTemplate(current, stayWithin); } -inline Node* next(const ContainerNode* current, const Node* stayWithin) { return traverseNextTemplate(current, stayWithin); } +inline Node* next(const Node& current, const Node* stayWithin) { return traverseNextTemplate(current, stayWithin); } +inline Node* next(const ContainerNode& current, const Node* stayWithin) { return traverseNextTemplate(current, stayWithin); } -template <class NodeType> -inline Node* traverseNextSkippingChildrenTemplate(NodeType* current) +inline Node* nextSkippingChildren(const Node& current) { - if (current->nextSibling()) - return current->nextSibling(); + if (current.nextSibling()) + return current.nextSibling(); return nextAncestorSibling(current); } -inline Node* nextSkippingChildren(const Node* current) { return traverseNextSkippingChildrenTemplate(current); } -inline Node* nextSkippingChildren(const ContainerNode* current) { return traverseNextSkippingChildrenTemplate(current); } -template <class NodeType> -inline Node* traverseNextSkippingChildrenTemplate(NodeType* current, const Node* stayWithin) +inline Node* nextSkippingChildren(const Node& current, const Node* stayWithin) { - if (current == stayWithin) - return 0; - if (current->nextSibling()) - return current->nextSibling(); + if (¤t == stayWithin) + return nullptr; + if (current.nextSibling()) + return current.nextSibling(); return nextAncestorSibling(current, stayWithin); } -inline Node* nextSkippingChildren(const Node* current, const Node* stayWithin) { return traverseNextSkippingChildrenTemplate(current, stayWithin); } -inline Node* nextSkippingChildren(const ContainerNode* current, const Node* stayWithin) { return traverseNextSkippingChildrenTemplate(current, stayWithin); } -inline Node* next(const Text* current) { return traverseNextSkippingChildrenTemplate(current); } -inline Node* next(const Text* current, const Node* stayWithin) { return traverseNextSkippingChildrenTemplate(current, stayWithin); } +inline Node* next(const Text& current) { return nextSkippingChildren(current); } +inline Node* next(const Text& current, const Node* stayWithin) { return nextSkippingChildren(current, stayWithin); } -inline Node* previous(const Node* current, const Node* stayWithin) +inline Node* previous(const Node& current, const Node* stayWithin) { - if (Node* previous = current->previousSibling()) - return deepLastChild(previous); - if (current->parentNode() == stayWithin) + if (Node* previous = current.previousSibling()) + return deepLastChild(*previous); + if (current.parentNode() == stayWithin) return nullptr; - return current->parentNode(); + return current.parentNode(); } } } - -#endif diff --git a/Source/WebCore/dom/NodeWithIndex.h b/Source/WebCore/dom/NodeWithIndex.h index 077b64047..6a30bda80 100644 --- a/Source/WebCore/dom/NodeWithIndex.h +++ b/Source/WebCore/dom/NodeWithIndex.h @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef NodeWithIndex_h -#define NodeWithIndex_h +#pragma once #include "Node.h" @@ -46,10 +45,10 @@ public: int index() const { if (!m_haveIndex) { - m_index = m_node->nodeIndex(); + m_index = m_node->computeNodeIndex(); m_haveIndex = true; } - ASSERT(m_index == static_cast<int>(m_node->nodeIndex())); + ASSERT(m_index == static_cast<int>(m_node->computeNodeIndex())); return m_index; } @@ -59,6 +58,4 @@ private: mutable int m_index; }; -} - -#endif +} // namespace WebCore diff --git a/Source/WebCore/dom/NonDocumentTypeChildNode.idl b/Source/WebCore/dom/NonDocumentTypeChildNode.idl new file mode 100644 index 000000000..70930235a --- /dev/null +++ b/Source/WebCore/dom/NonDocumentTypeChildNode.idl @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2015 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +// https://dom.spec.whatwg.org/#interface-nondocumenttypechildnode +[ + NoInterfaceObject, +] interface NonDocumentTypeChildNode { + readonly attribute Element previousElementSibling; + readonly attribute Element nextElementSibling; +}; diff --git a/Source/WebCore/dom/NonElementParentNode.idl b/Source/WebCore/dom/NonElementParentNode.idl new file mode 100644 index 000000000..92238703f --- /dev/null +++ b/Source/WebCore/dom/NonElementParentNode.idl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2015 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +// https://dom.spec.whatwg.org/#interface-nonelementparentnode +[ + NoInterfaceObject, +] interface NonElementParentNode { + [DOMJIT=ReadDOM] Element? getElementById([RequiresExistingAtomicString] DOMString elementId); +}; diff --git a/Source/WebCore/dom/Notation.cpp b/Source/WebCore/dom/Notation.cpp deleted file mode 100644 index 7cb5faa7a..000000000 --- a/Source/WebCore/dom/Notation.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2000 Peter Kelly (pmk@post.com) - * Copyright (C) 2006, 2009 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "config.h" -#include "Notation.h" - -#include "Document.h" - -namespace WebCore { - -Notation::Notation(Document& document, const String& name, const String& publicId, const String& systemId) - : ContainerNode(&document) - , m_name(name) - , m_publicId(publicId) - , m_systemId(systemId) -{ -} - -String Notation::nodeName() const -{ - return m_name; -} - -Node::NodeType Notation::nodeType() const -{ - return NOTATION_NODE; -} - -PassRefPtr<Node> Notation::cloneNode(bool /*deep*/) -{ - // Spec says cloning Notation nodes is "implementation dependent". We do not support it. - return 0; -} - -bool Notation::childTypeAllowed(NodeType) const -{ - return false; -} - -} // namespace diff --git a/Source/WebCore/dom/Notation.h b/Source/WebCore/dom/Notation.h deleted file mode 100644 index 59d0ef5ee..000000000 --- a/Source/WebCore/dom/Notation.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2000 Peter Kelly (pmk@post.com) - * Copyright (C) 2006, 2009 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef Notation_h -#define Notation_h - -#include "ContainerNode.h" - -namespace WebCore { - -// FIXME: This class is never instantiated. Maybe it should be removed. - -class Notation final : public ContainerNode { -public: - const String& publicId() const { return m_publicId; } - const String& systemId() const { return m_systemId; } - -private: - Notation(Document&, const String& name, const String& publicId, const String& systemId); - - virtual String nodeName() const override; - virtual NodeType nodeType() const override; - virtual PassRefPtr<Node> cloneNode(bool deep) override; - virtual bool childTypeAllowed(NodeType) const override; - - String m_name; - String m_publicId; - String m_systemId; -}; - -} //namespace - -#endif diff --git a/Source/WebCore/dom/Notation.idl b/Source/WebCore/dom/Notation.idl deleted file mode 100644 index 4ac628f05..000000000 --- a/Source/WebCore/dom/Notation.idl +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2006 Apple Computer, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -interface Notation : Node { - [TreatReturnedNullStringAs=Null] readonly attribute DOMString publicId; - [TreatReturnedNullStringAs=Null] readonly attribute DOMString systemId; -}; - diff --git a/Source/WebCore/dom/OverflowEvent.cpp b/Source/WebCore/dom/OverflowEvent.cpp index 1cc5edbc8..dc801784a 100644 --- a/Source/WebCore/dom/OverflowEvent.cpp +++ b/Source/WebCore/dom/OverflowEvent.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -30,16 +30,8 @@ namespace WebCore { -OverflowEventInit::OverflowEventInit() - : orient(0) - , horizontalOverflow(false) - , verticalOverflow(false) -{ -} - OverflowEvent::OverflowEvent() - : Event(eventNames().overflowchangedEvent, false, false) - , m_orient(VERTICAL) + : m_orient(VERTICAL) , m_horizontalOverflow(false) , m_verticalOverflow(false) { @@ -60,8 +52,8 @@ OverflowEvent::OverflowEvent(bool horizontalOverflowChanged, bool horizontalOver m_orient = VERTICAL; } -OverflowEvent::OverflowEvent(const AtomicString& type, const OverflowEventInit& initializer) - : Event(type, initializer) +OverflowEvent::OverflowEvent(const AtomicString& type, const Init& initializer, IsTrusted isTrusted) + : Event(type, initializer, isTrusted) , m_orient(initializer.orient) , m_horizontalOverflow(initializer.horizontalOverflow) , m_verticalOverflow(initializer.verticalOverflow) @@ -78,6 +70,8 @@ void OverflowEvent::initOverflowEvent(unsigned short orient, bool horizontalOver if (dispatched()) return; + initEvent(eventNames().overflowchangedEvent, false, false); + m_orient = orient; m_horizontalOverflow = horizontalOverflow; m_verticalOverflow = verticalOverflow; diff --git a/Source/WebCore/dom/OverflowEvent.h b/Source/WebCore/dom/OverflowEvent.h index ab6ca4585..f2da99578 100644 --- a/Source/WebCore/dom/OverflowEvent.h +++ b/Source/WebCore/dom/OverflowEvent.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -23,22 +23,13 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef OverflowEvent_h -#define OverflowEvent_h +#pragma once #include "Event.h" namespace WebCore { -struct OverflowEventInit : public EventInit { - OverflowEventInit(); - - unsigned short orient; - bool horizontalOverflow; - bool verticalOverflow; -}; - -class OverflowEvent : public Event { +class OverflowEvent final : public Event { public: enum orientType { HORIZONTAL = 0, @@ -46,37 +37,43 @@ public: BOTH = 2 }; - static PassRefPtr<OverflowEvent> create() + static Ref<OverflowEvent> create(bool horizontalOverflowChanged, bool horizontalOverflow, bool verticalOverflowChanged, bool verticalOverflow) { - return adoptRef(new OverflowEvent); + return adoptRef(*new OverflowEvent(horizontalOverflowChanged, horizontalOverflow, verticalOverflowChanged, verticalOverflow)); } - static PassRefPtr<OverflowEvent> create(bool horizontalOverflowChanged, bool horizontalOverflow, bool verticalOverflowChanged, bool verticalOverflow) + + static Ref<OverflowEvent> createForBindings() { - return adoptRef(new OverflowEvent(horizontalOverflowChanged, horizontalOverflow, verticalOverflowChanged, verticalOverflow)); + return adoptRef(*new OverflowEvent); } - static PassRefPtr<OverflowEvent> create(const AtomicString& type, const OverflowEventInit& initializer) + + struct Init : EventInit { + unsigned short orient { 0 }; + bool horizontalOverflow { false }; + bool verticalOverflow { false }; + }; + + static Ref<OverflowEvent> create(const AtomicString& type, const Init& initializer, IsTrusted isTrusted = IsTrusted::No) { - return adoptRef(new OverflowEvent(type, initializer)); + return adoptRef(*new OverflowEvent(type, initializer, isTrusted)); } - void initOverflowEvent(unsigned short orient, bool horizontalOverflow, bool verticalOverflow); + WEBCORE_EXPORT void initOverflowEvent(unsigned short orient, bool horizontalOverflow, bool verticalOverflow); unsigned short orient() const { return m_orient; } bool horizontalOverflow() const { return m_horizontalOverflow; } bool verticalOverflow() const { return m_verticalOverflow; } - virtual EventInterface eventInterface() const override; + EventInterface eventInterface() const override; private: OverflowEvent(); OverflowEvent(bool horizontalOverflowChanged, bool horizontalOverflow, bool verticalOverflowChanged, bool verticalOverflow); - OverflowEvent(const AtomicString&, const OverflowEventInit&); + OverflowEvent(const AtomicString&, const Init&, IsTrusted); unsigned short m_orient; bool m_horizontalOverflow; bool m_verticalOverflow; }; -} - -#endif // OverflowEvent_h +} // namespace WebCore diff --git a/Source/WebCore/dom/OverflowEvent.idl b/Source/WebCore/dom/OverflowEvent.idl index 4b9ecd3a3..9fe025f54 100644 --- a/Source/WebCore/dom/OverflowEvent.idl +++ b/Source/WebCore/dom/OverflowEvent.idl @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,20 +24,19 @@ */ [ - ConstructorTemplate=Event + Constructor(DOMString type, optional OverflowEventInit eventInitDict), ] interface OverflowEvent : Event { const unsigned short HORIZONTAL = 0; const unsigned short VERTICAL = 1; const unsigned short BOTH = 2; - [InitializedByEventConstructor] readonly attribute unsigned short orient; - [InitializedByEventConstructor] readonly attribute boolean horizontalOverflow; - [InitializedByEventConstructor] readonly attribute boolean verticalOverflow; - -#if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C - void initOverflowEvent([Default=Undefined] optional unsigned short orient, - [Default=Undefined] optional boolean horizontalOverflow, - [Default=Undefined] optional boolean verticalOverflow); -#endif + readonly attribute unsigned short orient; + readonly attribute boolean horizontalOverflow; + readonly attribute boolean verticalOverflow; }; +dictionary OverflowEventInit : EventInit { + unsigned short orient = 0; + boolean horizontalOverflow = false; + boolean verticalOverflow = false; +}; diff --git a/Source/WebCore/dom/PageTransitionEvent.cpp b/Source/WebCore/dom/PageTransitionEvent.cpp index 8ca9b2d9b..8d06cf407 100644 --- a/Source/WebCore/dom/PageTransitionEvent.cpp +++ b/Source/WebCore/dom/PageTransitionEvent.cpp @@ -26,28 +26,16 @@ #include "config.h" #include "PageTransitionEvent.h" -#include "EventNames.h" - namespace WebCore { -PageTransitionEventInit::PageTransitionEventInit() - : persisted(false) -{ -} - -PageTransitionEvent::PageTransitionEvent() - : m_persisted(false) -{ -} - PageTransitionEvent::PageTransitionEvent(const AtomicString& type, bool persisted) : Event(type, true, true) , m_persisted(persisted) { } -PageTransitionEvent::PageTransitionEvent(const AtomicString& type, const PageTransitionEventInit& initializer) - : Event(type, initializer) +PageTransitionEvent::PageTransitionEvent(const AtomicString& type, const Init& initializer, IsTrusted isTrusted) + : Event(type, initializer, isTrusted) , m_persisted(initializer.persisted) { } diff --git a/Source/WebCore/dom/PageTransitionEvent.h b/Source/WebCore/dom/PageTransitionEvent.h index 051a12c26..8442fa613 100644 --- a/Source/WebCore/dom/PageTransitionEvent.h +++ b/Source/WebCore/dom/PageTransitionEvent.h @@ -23,48 +23,39 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PageTransitionEvent_h -#define PageTransitionEvent_h +#pragma once #include "Event.h" namespace WebCore { -struct PageTransitionEventInit : public EventInit { - PageTransitionEventInit(); - - bool persisted; -}; - -class PageTransitionEvent : public Event { +class PageTransitionEvent final : public Event { public: - static PassRefPtr<PageTransitionEvent> create() - { - return adoptRef(new PageTransitionEvent); - } - static PassRefPtr<PageTransitionEvent> create(const AtomicString& type, bool persisted) + static Ref<PageTransitionEvent> create(const AtomicString& type, bool persisted) { - return adoptRef(new PageTransitionEvent(type, persisted)); + return adoptRef(*new PageTransitionEvent(type, persisted)); } - static PassRefPtr<PageTransitionEvent> create(const AtomicString& type, const PageTransitionEventInit& initializer) + + struct Init : EventInit { + bool persisted { false }; + }; + + static Ref<PageTransitionEvent> create(const AtomicString& type, const Init& initializer, IsTrusted isTrusted = IsTrusted::No) { - return adoptRef(new PageTransitionEvent(type, initializer)); + return adoptRef(*new PageTransitionEvent(type, initializer, isTrusted)); } virtual ~PageTransitionEvent(); - virtual EventInterface eventInterface() const override; + EventInterface eventInterface() const override; bool persisted() const { return m_persisted; } private: - PageTransitionEvent(); PageTransitionEvent(const AtomicString& type, bool persisted); - PageTransitionEvent(const AtomicString&, const PageTransitionEventInit&); + PageTransitionEvent(const AtomicString&, const Init&, IsTrusted); bool m_persisted; }; } // namespace WebCore - -#endif // PageTransitionEvent_h diff --git a/Source/WebCore/dom/PageTransitionEvent.idl b/Source/WebCore/dom/PageTransitionEvent.idl index c8cea7f6f..7f0491edd 100644 --- a/Source/WebCore/dom/PageTransitionEvent.idl +++ b/Source/WebCore/dom/PageTransitionEvent.idl @@ -24,8 +24,11 @@ */ [ - ConstructorTemplate=Event + Constructor(DOMString type, optional PageTransitionEventInit eventInitDict), ] interface PageTransitionEvent : Event { - [InitializedByEventConstructor] readonly attribute boolean persisted; + readonly attribute boolean persisted; }; +dictionary PageTransitionEventInit : EventInit { + boolean persisted = false; +}; diff --git a/Source/WebCore/dom/ParentNode.idl b/Source/WebCore/dom/ParentNode.idl new file mode 100644 index 000000000..eaf11dd8b --- /dev/null +++ b/Source/WebCore/dom/ParentNode.idl @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +// https://dom.spec.whatwg.org/#interface-parentnode +[ + NoInterfaceObject, +] interface ParentNode { + readonly attribute HTMLCollection children; + readonly attribute Element firstElementChild; + readonly attribute Element lastElementChild; + readonly attribute unsigned long childElementCount; + + [CEReactions, Unscopable, MayThrowException] void prepend((Node or DOMString)... nodes); + [CEReactions, Unscopable, MayThrowException] void append((Node or DOMString)... nodes); + + // [Unscopable] Element? query(DOMString relativeSelectors); + // [NewObject, Unscopable] Elements queryAll(DOMString relativeSelectors); + [MayThrowException] Element querySelector(DOMString selectors); + [MayThrowException, NewObject] NodeList querySelectorAll(DOMString selectors); +}; diff --git a/Source/WebCore/dom/PendingScript.cpp b/Source/WebCore/dom/PendingScript.cpp index c602a1126..2f986822a 100644 --- a/Source/WebCore/dom/PendingScript.cpp +++ b/Source/WebCore/dom/PendingScript.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * Copyright (C) 2011-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,43 +27,75 @@ #include "config.h" #include "PendingScript.h" -#include "CachedScript.h" -#include "Element.h" +#include "PendingScriptClient.h" +#include "ScriptElement.h" namespace WebCore { +Ref<PendingScript> PendingScript::create(ScriptElement& element, LoadableScript& loadableScript) +{ + auto pendingScript = adoptRef(*new PendingScript(element, loadableScript)); + loadableScript.addClient(pendingScript.get()); + return pendingScript; +} + +Ref<PendingScript> PendingScript::create(ScriptElement& element, TextPosition scriptStartPosition) +{ + return adoptRef(*new PendingScript(element, scriptStartPosition)); +} + +PendingScript::PendingScript(ScriptElement& element, TextPosition startingPosition) + : m_element(element) + , m_startingPosition(startingPosition) +{ +} + +PendingScript::PendingScript(ScriptElement& element, LoadableScript& loadableScript) + : m_element(element) + , m_loadableScript(&loadableScript) +{ +} + PendingScript::~PendingScript() { - if (m_cachedScript) - m_cachedScript->removeClient(this); + if (m_loadableScript) + m_loadableScript->removeClient(*this); +} + +void PendingScript::notifyClientFinished() +{ + Ref<PendingScript> protectedThis(*this); + if (m_client) + m_client->notifyFinished(*this); +} + +void PendingScript::notifyFinished(LoadableScript&) +{ + notifyClientFinished(); } -PassRefPtr<Element> PendingScript::releaseElementAndClear() +bool PendingScript::isLoaded() const { - setCachedScript(0); - m_watchingForLoad = false; - m_startingPosition = TextPosition::belowRangePosition(); - return m_element.release(); + return m_loadableScript && m_loadableScript->isLoaded(); } -void PendingScript::setCachedScript(CachedScript* cachedScript) +bool PendingScript::error() const { - if (m_cachedScript == cachedScript) - return; - if (m_cachedScript) - m_cachedScript->removeClient(this); - m_cachedScript = cachedScript; - if (m_cachedScript) - m_cachedScript->addClient(this); + return m_loadableScript && m_loadableScript->error(); } -CachedScript* PendingScript::cachedScript() const +void PendingScript::setClient(PendingScriptClient& client) { - return m_cachedScript.get(); + ASSERT(!m_client); + m_client = &client; + if (isLoaded()) + notifyClientFinished(); } -void PendingScript::notifyFinished(CachedResource*) +void PendingScript::clearClient() { + ASSERT(m_client); + m_client = nullptr; } } diff --git a/Source/WebCore/dom/PendingScript.h b/Source/WebCore/dom/PendingScript.h index f7831ce6f..fc758d07d 100644 --- a/Source/WebCore/dom/PendingScript.h +++ b/Source/WebCore/dom/PendingScript.h @@ -23,86 +23,63 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PendingScript_h -#define PendingScript_h +#pragma once -#include "CachedResourceClient.h" -#include "CachedResourceHandle.h" +#include "LoadableScript.h" +#include "LoadableScriptClient.h" +#include <wtf/Ref.h> +#include <wtf/RefCounted.h> #include <wtf/text/TextPosition.h> -#include <wtf/PassRefPtr.h> -#include <wtf/RefPtr.h> namespace WebCore { class CachedScript; -class Element; - -// A container for an external script which may be loaded and executed. -// -// A CachedResourceHandle alone does not prevent the underlying CachedResource -// from purging its data buffer. This class holds a dummy client open for its -// lifetime in order to guarantee that the data buffer will not be purged. -class PendingScript : public CachedResourceClient { +class PendingScriptClient; +class ScriptElement; + +// A container for scripts which may be loaded and executed. +// This can hold LoadableScript and non external inline script. +class PendingScript final : public RefCounted<PendingScript>, public LoadableScriptClient { public: - PendingScript() - : m_watchingForLoad(false) - , m_startingPosition(TextPosition::belowRangePosition()) - { - } - - PendingScript(Element* element, CachedScript* cachedScript) - : m_watchingForLoad(false) - , m_element(element) - { - setCachedScript(cachedScript); - } - - PendingScript(const PendingScript& other) - : CachedResourceClient(other) - , m_watchingForLoad(other.m_watchingForLoad) - , m_element(other.m_element) - , m_startingPosition(other.m_startingPosition) - { - setCachedScript(other.cachedScript()); - } - - ~PendingScript(); - - PendingScript& operator=(const PendingScript& other) - { - if (this == &other) - return *this; - - m_watchingForLoad = other.m_watchingForLoad; - m_element = other.m_element; - m_startingPosition = other.m_startingPosition; - setCachedScript(other.cachedScript()); - - return *this; - } + static Ref<PendingScript> create(ScriptElement&, LoadableScript&); + static Ref<PendingScript> create(ScriptElement&, TextPosition scriptStartPosition); + + virtual ~PendingScript(); TextPosition startingPosition() const { return m_startingPosition; } void setStartingPosition(const TextPosition& position) { m_startingPosition = position; } - bool watchingForLoad() const { return m_watchingForLoad; } - void setWatchingForLoad(bool b) { m_watchingForLoad = b; } + bool watchingForLoad() const { return needsLoading() && m_client; } + + ScriptElement& element() { return m_element.get(); } + const ScriptElement& element() const { return m_element.get(); } + + LoadableScript* loadableScript() const; + bool needsLoading() const { return loadableScript(); } - Element* element() const { return m_element.get(); } - void setElement(Element* element) { m_element = element; } - PassRefPtr<Element> releaseElementAndClear(); + bool isLoaded() const; + bool error() const; - CachedScript* cachedScript() const; - void setCachedScript(CachedScript*); + void notifyFinished(LoadableScript&) override; - virtual void notifyFinished(CachedResource*) override; + void setClient(PendingScriptClient&); + void clearClient(); private: - bool m_watchingForLoad; - RefPtr<Element> m_element; + PendingScript(ScriptElement&, LoadableScript&); + PendingScript(ScriptElement&, TextPosition startingPosition); + + void notifyClientFinished(); + + Ref<ScriptElement> m_element; TextPosition m_startingPosition; // Only used for inline script tags. - CachedResourceHandle<CachedScript> m_cachedScript; + RefPtr<LoadableScript> m_loadableScript; + PendingScriptClient* m_client { nullptr }; }; +inline LoadableScript* PendingScript::loadableScript() const +{ + return m_loadableScript.get(); } -#endif +} diff --git a/Source/WebCore/dom/PendingScriptClient.h b/Source/WebCore/dom/PendingScriptClient.h new file mode 100644 index 000000000..e96444a8e --- /dev/null +++ b/Source/WebCore/dom/PendingScriptClient.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016 Apple, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +namespace WebCore { + +class PendingScript; + +class PendingScriptClient { +public: + virtual ~PendingScriptClient() { } + + virtual void notifyFinished(PendingScript&) = 0; +}; + +} diff --git a/Source/WebCore/dom/PopStateEvent.cpp b/Source/WebCore/dom/PopStateEvent.cpp index 693782c42..e4c6437a5 100644 --- a/Source/WebCore/dom/PopStateEvent.cpp +++ b/Source/WebCore/dom/PopStateEvent.cpp @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY APPLE, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -29,53 +29,52 @@ #include "EventNames.h" #include "History.h" -#include "SerializedScriptValue.h" +#include <runtime/JSCInlines.h> namespace WebCore { -PopStateEventInit::PopStateEventInit() +PopStateEvent::PopStateEvent(JSC::ExecState& state, const AtomicString& type, const Init& initializer, IsTrusted isTrusted) + : Event(type, initializer, isTrusted) + , m_state(state.vm(), initializer.state) { } -PopStateEvent::PopStateEvent() +PopStateEvent::PopStateEvent(RefPtr<SerializedScriptValue>&& serializedState, History* history) : Event(eventNames().popstateEvent, false, true) - , m_serializedState(0) - , m_history(0) -{ -} - -PopStateEvent::PopStateEvent(const AtomicString& type, const PopStateEventInit& initializer) - : Event(type, initializer) - , m_state(initializer.state) - , m_serializedState(0) - , m_history(0) + , m_serializedState(WTFMove(serializedState)) + , m_history(history) { } -PopStateEvent::PopStateEvent(PassRefPtr<SerializedScriptValue> serializedState, PassRefPtr<History> history) - : Event(eventNames().popstateEvent, false, true) - , m_serializedState(serializedState) - , m_history(history) +PopStateEvent::~PopStateEvent() { } -PopStateEvent::~PopStateEvent() +Ref<PopStateEvent> PopStateEvent::create(RefPtr<SerializedScriptValue>&& serializedState, History* history) { + return adoptRef(*new PopStateEvent(WTFMove(serializedState), history)); } -PassRefPtr<PopStateEvent> PopStateEvent::create() +Ref<PopStateEvent> PopStateEvent::create(JSC::ExecState& state, const AtomicString& type, const Init& initializer, IsTrusted isTrusted) { - return adoptRef(new PopStateEvent); + return adoptRef(*new PopStateEvent(state, type, initializer, isTrusted)); } -PassRefPtr<PopStateEvent> PopStateEvent::create(PassRefPtr<SerializedScriptValue> serializedState, PassRefPtr<History> history) +Ref<PopStateEvent> PopStateEvent::createForBindings() { - return adoptRef(new PopStateEvent(serializedState, history)); + return adoptRef(*new PopStateEvent); } -PassRefPtr<PopStateEvent> PopStateEvent::create(const AtomicString& type, const PopStateEventInit& initializer) +RefPtr<SerializedScriptValue> PopStateEvent::trySerializeState(JSC::ExecState& executionState) { - return adoptRef(new PopStateEvent(type, initializer)); + ASSERT(!m_state.hasNoValue()); + + if (!m_serializedState && !m_triedToSerialize) { + m_serializedState = SerializedScriptValue::create(executionState, m_state.jsValue(), SerializationErrorMode::NonThrowing); + m_triedToSerialize = true; + } + + return m_serializedState; } EventInterface PopStateEvent::eventInterface() const diff --git a/Source/WebCore/dom/PopStateEvent.h b/Source/WebCore/dom/PopStateEvent.h index b2a4f703b..2bdff6926 100644 --- a/Source/WebCore/dom/PopStateEvent.h +++ b/Source/WebCore/dom/PopStateEvent.h @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY APPLE, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,46 +24,46 @@ * */ -#ifndef PopStateEvent_h -#define PopStateEvent_h +#pragma once #include "Event.h" #include <bindings/ScriptValue.h> namespace WebCore { -struct PopStateEventInit : public EventInit { - PopStateEventInit(); - - Deprecated::ScriptValue state; -}; - class History; class SerializedScriptValue; -class PopStateEvent : public Event { +class PopStateEvent final : public Event { public: virtual ~PopStateEvent(); - static PassRefPtr<PopStateEvent> create(); - static PassRefPtr<PopStateEvent> create(PassRefPtr<SerializedScriptValue>, PassRefPtr<History>); - static PassRefPtr<PopStateEvent> create(const AtomicString&, const PopStateEventInit&); + static Ref<PopStateEvent> create(RefPtr<SerializedScriptValue>&&, History*); - PassRefPtr<SerializedScriptValue> serializedState() const { return m_serializedState; } - const Deprecated::ScriptValue& state() const { return m_state; } - History* history() const { return m_history.get(); } + struct Init : EventInit { + JSC::JSValue state; + }; + + static Ref<PopStateEvent> create(JSC::ExecState&, const AtomicString&, const Init&, IsTrusted = IsTrusted::No); + static Ref<PopStateEvent> createForBindings(); - virtual EventInterface eventInterface() const override; + JSC::JSValue state() const { return m_state; } + SerializedScriptValue* serializedState() const { return m_serializedState.get(); } + + RefPtr<SerializedScriptValue> trySerializeState(JSC::ExecState&); + + History* history() const { return m_history.get(); } private: - PopStateEvent(); - PopStateEvent(const AtomicString&, const PopStateEventInit&); - explicit PopStateEvent(PassRefPtr<SerializedScriptValue>, PassRefPtr<History>); + PopStateEvent() = default; + PopStateEvent(JSC::ExecState&, const AtomicString&, const Init&, IsTrusted); + PopStateEvent(RefPtr<SerializedScriptValue>&&, History*); + + EventInterface eventInterface() const final; Deprecated::ScriptValue m_state; RefPtr<SerializedScriptValue> m_serializedState; + bool m_triedToSerialize { false }; RefPtr<History> m_history; }; } // namespace WebCore - -#endif // PopStateEvent_h diff --git a/Source/WebCore/dom/PopStateEvent.idl b/Source/WebCore/dom/PopStateEvent.idl index 04d9409fd..fc22ba889 100644 --- a/Source/WebCore/dom/PopStateEvent.idl +++ b/Source/WebCore/dom/PopStateEvent.idl @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY APPLE, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,11 +24,14 @@ * */ -#if !defined(LANGUAGE_CPP) || !LANGUAGE_CPP [ - ConstructorTemplate=Event + Constructor(DOMString type, optional PopStateEventInit eventInitDict), + ConstructorCallWith=ScriptState, ] interface PopStateEvent : Event { - [InitializedByEventConstructor, CachedAttribute, CustomGetter] readonly attribute any state; + [CachedAttribute, CustomGetter] readonly attribute any state; +}; + +dictionary PopStateEventInit : EventInit { + any state = null; }; -#endif diff --git a/Source/WebCore/dom/Position.cpp b/Source/WebCore/dom/Position.cpp index 287b37b2b..10c4b540d 100644 --- a/Source/WebCore/dom/Position.cpp +++ b/Source/WebCore/dom/Position.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -27,6 +27,9 @@ #include "Position.h" #include "CSSComputedStyleDeclaration.h" +#include "HTMLBRElement.h" +#include "HTMLBodyElement.h" +#include "HTMLHtmlElement.h" #include "HTMLNames.h" #include "HTMLTableElement.h" #include "InlineElementBox.h" @@ -35,30 +38,38 @@ #include "Logging.h" #include "PositionIterator.h" #include "RenderBlock.h" +#include "RenderFlexibleBox.h" +#include "RenderGrid.h" #include "RenderInline.h" +#include "RenderIterator.h" #include "RenderLineBreak.h" #include "RenderText.h" #include "RuntimeEnabledFeatures.h" #include "Text.h" #include "TextIterator.h" +#include "TextStream.h" #include "VisiblePosition.h" #include "VisibleUnits.h" #include "htmlediting.h" #include <stdio.h> #include <wtf/text/CString.h> #include <wtf/unicode/CharacterNames.h> - + +#if ENABLE(TREE_DEBUGGING) +#include <wtf/text/StringBuilder.h> +#endif + namespace WebCore { using namespace HTMLNames; static bool hasInlineBoxWrapper(RenderObject& renderer) { - if (renderer.isBox() && toRenderBox(renderer).inlineBoxWrapper()) + if (is<RenderBox>(renderer) && downcast<RenderBox>(renderer).inlineBoxWrapper()) return true; - if (renderer.isText() && toRenderText(renderer).firstTextBox()) + if (is<RenderText>(renderer) && downcast<RenderText>(renderer).firstTextBox()) return true; - if (renderer.isLineBreak() && toRenderLineBreak(renderer).inlineBoxWrapper()) + if (is<RenderLineBreak>(renderer) && downcast<RenderLineBreak>(renderer).inlineBoxWrapper()) return true; return false; } @@ -72,7 +83,7 @@ static Node* nextRenderedEditable(Node* node) if (hasInlineBoxWrapper(*renderer)) return node; } - return 0; + return nullptr; } static Node* previousRenderedEditable(Node* node) @@ -84,72 +95,55 @@ static Node* previousRenderedEditable(Node* node) if (hasInlineBoxWrapper(*renderer)) return node; } - return 0; + return nullptr; } -Position::Position(PassRefPtr<Node> anchorNode, LegacyEditingOffset offset) +Position::Position(Node* anchorNode, unsigned offset, LegacyEditingPositionFlag) : m_anchorNode(anchorNode) - , m_offset(offset.value()) + , m_offset(offset) , m_anchorType(anchorTypeForLegacyEditingPosition(m_anchorNode.get(), m_offset)) , m_isLegacyEditingPosition(true) { -#if ENABLE(SHADOW_DOM) - ASSERT((m_anchorNode && RuntimeEnabledFeatures::sharedFeatures().shadowDOMEnabled()) || !m_anchorNode || !m_anchorNode->isShadowRoot() || m_anchorNode == containerNode()); -#else ASSERT(!m_anchorNode || !m_anchorNode->isShadowRoot() || m_anchorNode == containerNode()); -#endif ASSERT(!m_anchorNode || !m_anchorNode->isPseudoElement()); } -Position::Position(PassRefPtr<Node> anchorNode, AnchorType anchorType) +Position::Position(Node* anchorNode, AnchorType anchorType) : m_anchorNode(anchorNode) , m_offset(0) , m_anchorType(anchorType) , m_isLegacyEditingPosition(false) { -#if ENABLE(SHADOW_DOM) - ASSERT((m_anchorNode && RuntimeEnabledFeatures::sharedFeatures().shadowDOMEnabled()) || !m_anchorNode || !m_anchorNode->isShadowRoot() || m_anchorNode == containerNode()); -#else ASSERT(!m_anchorNode || !m_anchorNode->isShadowRoot() || m_anchorNode == containerNode()); -#endif - ASSERT(!m_anchorNode || !m_anchorNode->isPseudoElement()); - ASSERT(anchorType != PositionIsOffsetInAnchor); ASSERT(!((anchorType == PositionIsBeforeChildren || anchorType == PositionIsAfterChildren) - && (m_anchorNode->isTextNode() || editingIgnoresContent(m_anchorNode.get())))); + && (is<Text>(*m_anchorNode) || editingIgnoresContent(*m_anchorNode)))); } -Position::Position(PassRefPtr<Node> anchorNode, int offset, AnchorType anchorType) +Position::Position(Node* anchorNode, int offset, AnchorType anchorType) : m_anchorNode(anchorNode) , m_offset(offset) , m_anchorType(anchorType) , m_isLegacyEditingPosition(false) { -#if ENABLE(SHADOW_DOM) - ASSERT((m_anchorNode && RuntimeEnabledFeatures::sharedFeatures().shadowDOMEnabled()) - || !m_anchorNode || !editingIgnoresContent(m_anchorNode.get()) || !m_anchorNode->isShadowRoot()); -#else - ASSERT(!m_anchorNode || !editingIgnoresContent(m_anchorNode.get()) || !m_anchorNode->isShadowRoot()); -#endif - + ASSERT(!m_anchorNode || !editingIgnoresContent(*m_anchorNode)); ASSERT(!m_anchorNode || !m_anchorNode->isPseudoElement()); - ASSERT(anchorType == PositionIsOffsetInAnchor); } -Position::Position(PassRefPtr<Text> textNode, unsigned offset) +Position::Position(Text* textNode, unsigned offset) : m_anchorNode(textNode) - , m_offset(static_cast<int>(offset)) + , m_offset(offset) , m_anchorType(PositionIsOffsetInAnchor) , m_isLegacyEditingPosition(false) { ASSERT(m_anchorNode); } -void Position::moveToPosition(PassRefPtr<Node> node, int offset) +void Position::moveToPosition(Node* node, int offset) { - ASSERT(!editingIgnoresContent(node.get())); + ASSERT(!editingIgnoresContent(*node)); ASSERT(anchorType() == PositionIsOffsetInAnchor || m_isLegacyEditingPosition); m_anchorNode = node; m_offset = offset; @@ -167,7 +161,7 @@ void Position::moveToOffset(int offset) Node* Position::containerNode() const { if (!m_anchorNode) - return 0; + return nullptr; switch (anchorType()) { case PositionIsBeforeChildren: @@ -176,27 +170,27 @@ Node* Position::containerNode() const return m_anchorNode.get(); case PositionIsBeforeAnchor: case PositionIsAfterAnchor: - return findParent(m_anchorNode.get()); + return m_anchorNode->parentNode(); } ASSERT_NOT_REACHED(); - return 0; + return nullptr; } Text* Position::containerText() const { switch (anchorType()) { case PositionIsOffsetInAnchor: - return m_anchorNode && m_anchorNode->isTextNode() ? toText(m_anchorNode.get()) : 0; + return m_anchorNode && is<Text>(*m_anchorNode) ? downcast<Text>(m_anchorNode.get()) : nullptr; case PositionIsBeforeAnchor: case PositionIsAfterAnchor: - return 0; + return nullptr; case PositionIsBeforeChildren: case PositionIsAfterChildren: - ASSERT(!m_anchorNode || !m_anchorNode->isTextNode()); - return 0; + ASSERT(!m_anchorNode || !is<Text>(*m_anchorNode)); + return nullptr; } ASSERT_NOT_REACHED(); - return 0; + return nullptr; } int Position::computeOffsetInContainerNode() const @@ -212,9 +206,9 @@ int Position::computeOffsetInContainerNode() const case PositionIsOffsetInAnchor: return minOffsetForNode(m_anchorNode.get(), m_offset); case PositionIsBeforeAnchor: - return m_anchorNode->nodeIndex(); + return m_anchorNode->computeNodeIndex(); case PositionIsAfterAnchor: - return m_anchorNode->nodeIndex() + 1; + return m_anchorNode->computeNodeIndex() + 1; } ASSERT_NOT_REACHED(); return 0; @@ -224,7 +218,8 @@ int Position::offsetForPositionAfterAnchor() const { ASSERT(m_anchorType == PositionIsAfterAnchor || m_anchorType == PositionIsAfterChildren); ASSERT(!m_isLegacyEditingPosition); - return lastOffsetForEditing(m_anchorNode.get()); + ASSERT(m_anchorNode); + return m_anchorNode ? lastOffsetForEditing(*m_anchorNode) : 0; } // Neighbor-anchored positions are invalid DOM positions, so they need to be @@ -232,69 +227,70 @@ int Position::offsetForPositionAfterAnchor() const Position Position::parentAnchoredEquivalent() const { if (!m_anchorNode) - return Position(); + return { }; // FIXME: This should only be necessary for legacy positions, but is also needed for positions before and after Tables if (m_offset <= 0 && (m_anchorType != PositionIsAfterAnchor && m_anchorType != PositionIsAfterChildren)) { - if (findParent(m_anchorNode.get()) && (editingIgnoresContent(m_anchorNode.get()) || isTableElement(m_anchorNode.get()))) + if (m_anchorNode->parentNode() && (editingIgnoresContent(*m_anchorNode) || isRenderedTable(m_anchorNode.get()))) return positionInParentBeforeNode(m_anchorNode.get()); return Position(m_anchorNode.get(), 0, PositionIsOffsetInAnchor); } + if (!m_anchorNode->offsetInCharacters() - && (m_anchorType == PositionIsAfterAnchor || m_anchorType == PositionIsAfterChildren || static_cast<unsigned>(m_offset) == m_anchorNode->childNodeCount()) - && (editingIgnoresContent(m_anchorNode.get()) || isTableElement(m_anchorNode.get())) + && (m_anchorType == PositionIsAfterAnchor || m_anchorType == PositionIsAfterChildren || static_cast<unsigned>(m_offset) == m_anchorNode->countChildNodes()) + && (editingIgnoresContent(*m_anchorNode) || isRenderedTable(m_anchorNode.get())) && containerNode()) { return positionInParentAfterNode(m_anchorNode.get()); } - return Position(containerNode(), computeOffsetInContainerNode(), PositionIsOffsetInAnchor); + return { containerNode(), computeOffsetInContainerNode(), PositionIsOffsetInAnchor }; } Node* Position::computeNodeBeforePosition() const { if (!m_anchorNode) - return 0; + return nullptr; switch (anchorType()) { case PositionIsBeforeChildren: - return 0; + return nullptr; case PositionIsAfterChildren: return m_anchorNode->lastChild(); case PositionIsOffsetInAnchor: - return m_anchorNode->childNode(m_offset - 1); // -1 converts to childNode((unsigned)-1) and returns null. + return m_offset ? m_anchorNode->traverseToChildAt(m_offset - 1) : nullptr; case PositionIsBeforeAnchor: return m_anchorNode->previousSibling(); case PositionIsAfterAnchor: return m_anchorNode.get(); } ASSERT_NOT_REACHED(); - return 0; + return nullptr; } Node* Position::computeNodeAfterPosition() const { if (!m_anchorNode) - return 0; + return nullptr; switch (anchorType()) { case PositionIsBeforeChildren: return m_anchorNode->firstChild(); case PositionIsAfterChildren: - return 0; + return nullptr; case PositionIsOffsetInAnchor: - return m_anchorNode->childNode(m_offset); + return m_anchorNode->traverseToChildAt(m_offset); case PositionIsBeforeAnchor: return m_anchorNode.get(); case PositionIsAfterAnchor: return m_anchorNode->nextSibling(); } ASSERT_NOT_REACHED(); - return 0; + return nullptr; } Position::AnchorType Position::anchorTypeForLegacyEditingPosition(Node* anchorNode, int offset) { - if (anchorNode && editingIgnoresContent(anchorNode)) { + if (anchorNode && editingIgnoresContent(*anchorNode)) { if (offset == 0) return Position::PositionIsBeforeAnchor; return Position::PositionIsAfterAnchor; @@ -305,25 +301,32 @@ Position::AnchorType Position::anchorTypeForLegacyEditingPosition(Node* anchorNo // FIXME: This method is confusing (does it return anchorNode() or containerNode()?) and should be renamed or removed Element* Position::element() const { - Node* n = anchorNode(); - while (n && !n->isElementNode()) - n = n->parentNode(); - return toElement(n); + Node* node = anchorNode(); + while (node && !is<Element>(*node)) + node = node->parentNode(); + return downcast<Element>(node); } Position Position::previous(PositionMoveType moveType) const { - Node* n = deprecatedNode(); - if (!n) + Node* node = deprecatedNode(); + if (!node) return *this; - int o = deprecatedEditingOffset(); + int offset = deprecatedEditingOffset(); // FIXME: Negative offsets shouldn't be allowed. We should catch this earlier. - ASSERT(o >= 0); + ASSERT(offset >= 0); - if (o > 0) { - Node* child = n->childNode(o - 1); - if (child) + if (anchorType() == PositionIsBeforeAnchor) { + node = containerNode(); + if (!node) + return *this; + + offset = computeOffsetInContainerNode(); + } + + if (offset > 0) { + if (Node* child = node->traverseToChildAt(offset - 1)) return lastPositionInOrAfterNode(child); // There are two reasons child might be 0: @@ -333,35 +336,50 @@ Position Position::previous(PositionMoveType moveType) const // Going from 1 to 0 is correct. switch (moveType) { case CodePoint: - return createLegacyEditingPosition(n, o - 1); + return createLegacyEditingPosition(node, offset - 1); case Character: - return createLegacyEditingPosition(n, uncheckedPreviousOffset(n, o)); + return createLegacyEditingPosition(node, uncheckedPreviousOffset(node, offset)); case BackwardDeletion: - return createLegacyEditingPosition(n, uncheckedPreviousOffsetForBackwardDeletion(n, o)); + return createLegacyEditingPosition(node, uncheckedPreviousOffsetForBackwardDeletion(node, offset)); } } - ContainerNode* parent = findParent(n); + ContainerNode* parent = node->parentNode(); if (!parent) return *this; - return createLegacyEditingPosition(parent, n->nodeIndex()); + if (positionBeforeOrAfterNodeIsCandidate(*node)) + return positionBeforeNode(node); + + Node* previousSibling = node->previousSibling(); + if (previousSibling && positionBeforeOrAfterNodeIsCandidate(*previousSibling)) + return positionAfterNode(previousSibling); + + return createLegacyEditingPosition(parent, node->computeNodeIndex()); } Position Position::next(PositionMoveType moveType) const { ASSERT(moveType != BackwardDeletion); - Node* n = deprecatedNode(); - if (!n) + Node* node = deprecatedNode(); + if (!node) return *this; - int o = deprecatedEditingOffset(); + int offset = deprecatedEditingOffset(); // FIXME: Negative offsets shouldn't be allowed. We should catch this earlier. - ASSERT(o >= 0); + ASSERT(offset >= 0); + + if (anchorType() == PositionIsAfterAnchor) { + node = containerNode(); + if (!node) + return *this; - Node* child = n->childNode(o); - if (child || (!n->hasChildNodes() && o < lastOffsetForEditing(n))) { + offset = computeOffsetInContainerNode(); + } + + Node* child = node->traverseToChildAt(offset); + if (child || (!node->hasChildNodes() && offset < lastOffsetForEditing(*node))) { if (child) return firstPositionInOrBeforeNode(child); @@ -370,14 +388,21 @@ Position Position::next(PositionMoveType moveType) const // Going forward one character at a time is correct. // 2) The new offset is a bogus offset like (<br>, 1), and there is no child. // Going from 0 to 1 is correct. - return createLegacyEditingPosition(n, (moveType == Character) ? uncheckedNextOffset(n, o) : o + 1); + return createLegacyEditingPosition(node, (moveType == Character) ? uncheckedNextOffset(node, offset) : offset + 1); } - ContainerNode* parent = findParent(n); + ContainerNode* parent = node->parentNode(); if (!parent) return *this; - return createLegacyEditingPosition(parent, n->nodeIndex() + 1); + if (isRenderedTable(node) || editingIgnoresContent(*node)) + return positionAfterNode(node); + + Node* nextSibling = node->nextSibling(); + if (nextSibling && positionBeforeOrAfterNodeIsCandidate(*nextSibling)) + return positionBeforeNode(nextSibling); + + return createLegacyEditingPosition(parent, node->computeNodeIndex() + 1); } int Position::uncheckedPreviousOffset(const Node* n, int current) @@ -409,7 +434,7 @@ bool Position::atFirstEditingPositionForNode() const return true; case PositionIsAfterChildren: case PositionIsAfterAnchor: - return !lastOffsetForEditing(deprecatedNode()); + return !lastOffsetForEditing(*deprecatedNode()); } ASSERT_NOT_REACHED(); return false; @@ -421,7 +446,7 @@ bool Position::atLastEditingPositionForNode() const return true; // FIXME: Position after anchor shouldn't be considered as at the first editing position for node // since that position resides outside of the node. - return m_anchorType == PositionIsAfterAnchor || m_anchorType == PositionIsAfterChildren || m_offset >= lastOffsetForEditing(deprecatedNode()); + return m_anchorType == PositionIsAfterAnchor || m_anchorType == PositionIsAfterChildren || m_offset >= lastOffsetForEditing(*deprecatedNode()); } // A position is considered at editing boundary if one of the following is true: @@ -448,11 +473,11 @@ bool Position::atEditingBoundary() const Node* Position::parentEditingBoundary() const { if (!m_anchorNode) - return 0; + return nullptr; Node* documentElement = m_anchorNode->document().documentElement(); if (!documentElement) - return 0; + return nullptr; Node* boundary = m_anchorNode.get(); while (boundary != documentElement && boundary->nonShadowBoundaryParentNode() && m_anchorNode->hasEditableStyle() == boundary->parentNode()->hasEditableStyle()) @@ -466,39 +491,75 @@ bool Position::atStartOfTree() const { if (isNull()) return true; - return !findParent(deprecatedNode()) && m_offset <= 0; + + Node* container = containerNode(); + if (container && container->parentNode()) + return false; + + switch (m_anchorType) { + case PositionIsOffsetInAnchor: + return m_offset <= 0; + case PositionIsBeforeAnchor: + return !m_anchorNode->previousSibling(); + case PositionIsAfterAnchor: + return false; + case PositionIsBeforeChildren: + return true; + case PositionIsAfterChildren: + return !lastOffsetForEditing(*m_anchorNode); + } + ASSERT_NOT_REACHED(); + return false; } bool Position::atEndOfTree() const { if (isNull()) return true; - return !findParent(deprecatedNode()) && m_offset >= lastOffsetForEditing(deprecatedNode()); + + Node* container = containerNode(); + if (container && container->parentNode()) + return false; + + switch (m_anchorType) { + case PositionIsOffsetInAnchor: + return m_offset >= lastOffsetForEditing(*m_anchorNode); + case PositionIsBeforeAnchor: + return false; + case PositionIsAfterAnchor: + return !m_anchorNode->nextSibling(); + case PositionIsBeforeChildren: + return !lastOffsetForEditing(*m_anchorNode); + case PositionIsAfterChildren: + return true; + } + ASSERT_NOT_REACHED(); + return false; } // return first preceding DOM position rendered at a different location, or "this" Position Position::previousCharacterPosition(EAffinity affinity) const { if (isNull()) - return Position(); + return { }; Node* fromRootEditableElement = deprecatedNode()->rootEditableElement(); bool atStartOfLine = isStartOfLine(VisiblePosition(*this, affinity)); bool rendered = isCandidate(); - Position currentPos = *this; - while (!currentPos.atStartOfTree()) { - currentPos = currentPos.previous(); + Position currentPosition = *this; + while (!currentPosition.atStartOfTree()) { + currentPosition = currentPosition.previous(); - if (currentPos.deprecatedNode()->rootEditableElement() != fromRootEditableElement) + if (currentPosition.deprecatedNode()->rootEditableElement() != fromRootEditableElement) return *this; if (atStartOfLine || !rendered) { - if (currentPos.isCandidate()) - return currentPos; - } else if (rendersInDifferentPosition(currentPos)) - return currentPos; + if (currentPosition.isCandidate()) + return currentPosition; + } else if (rendersInDifferentPosition(currentPosition)) + return currentPosition; } return *this; @@ -508,25 +569,25 @@ Position Position::previousCharacterPosition(EAffinity affinity) const Position Position::nextCharacterPosition(EAffinity affinity) const { if (isNull()) - return Position(); + return { }; Node* fromRootEditableElement = deprecatedNode()->rootEditableElement(); - bool atEndOfLine = isEndOfLine(VisiblePosition(*this, affinity)); + bool atEndOfLine = isEndOfLine({ *this, affinity }); bool rendered = isCandidate(); - Position currentPos = *this; - while (!currentPos.atEndOfTree()) { - currentPos = currentPos.next(); + Position currentPosition = *this; + while (!currentPosition.atEndOfTree()) { + currentPosition = currentPosition.next(); - if (currentPos.deprecatedNode()->rootEditableElement() != fromRootEditableElement) + if (currentPosition.deprecatedNode()->rootEditableElement() != fromRootEditableElement) return *this; if (atEndOfLine || !rendered) { - if (currentPos.isCandidate()) - return currentPos; - } else if (rendersInDifferentPosition(currentPos)) - return currentPos; + if (currentPosition.isCandidate()) + return currentPosition; + } else if (rendersInDifferentPosition(currentPosition)) + return currentPosition; } return *this; @@ -545,11 +606,11 @@ static bool endsOfNodeAreVisuallyDistinctPositions(Node* node) return true; // Don't include inline tables. - if (isHTMLTableElement(node)) + if (is<HTMLTableElement>(*node)) return false; // There is a VisiblePosition inside an empty inline-block container. - return node->renderer()->isReplaced() && canHaveChildrenForEditing(node) && toRenderBox(node->renderer())->height() != 0 && !node->firstChild(); + return node->renderer()->isReplaced() && canHaveChildrenForEditing(*node) && downcast<RenderBox>(*node->renderer()).height() && !node->firstChild(); } static Node* enclosingVisualBoundary(Node* node) @@ -583,85 +644,85 @@ Position Position::upstream(EditingBoundaryCrossingRule rule) const { Node* startNode = deprecatedNode(); if (!startNode) - return Position(); + return { }; // iterate backward from there, looking for a qualified position Node* boundary = enclosingVisualBoundary(startNode); // FIXME: PositionIterator should respect Before and After positions. - PositionIterator lastVisible = m_anchorType == PositionIsAfterAnchor ? createLegacyEditingPosition(m_anchorNode.get(), caretMaxOffset(m_anchorNode.get())) : *this; - PositionIterator currentPos = lastVisible; + PositionIterator lastVisible = m_anchorType == PositionIsAfterAnchor ? createLegacyEditingPosition(m_anchorNode.get(), caretMaxOffset(*m_anchorNode)) : *this; + PositionIterator currentPosition = lastVisible; bool startEditable = startNode->hasEditableStyle(); Node* lastNode = startNode; bool boundaryCrossed = false; - for (; !currentPos.atStart(); currentPos.decrement()) { - Node* currentNode = currentPos.node(); + for (; !currentPosition.atStart(); currentPosition.decrement()) { + auto& currentNode = *currentPosition.node(); // Don't check for an editability change if we haven't moved to a different node, // to avoid the expense of computing hasEditableStyle(). - if (currentNode != lastNode) { + if (¤tNode != lastNode) { // Don't change editability. - bool currentEditable = currentNode->hasEditableStyle(); + bool currentEditable = currentNode.hasEditableStyle(); if (startEditable != currentEditable) { if (rule == CannotCrossEditingBoundary) break; boundaryCrossed = true; } - lastNode = currentNode; + lastNode = ¤tNode; } // If we've moved to a position that is visually distinct, return the last saved position. There // is code below that terminates early if we're *about* to move to a visually distinct position. - if (endsOfNodeAreVisuallyDistinctPositions(currentNode) && currentNode != boundary) + if (endsOfNodeAreVisuallyDistinctPositions(¤tNode) && ¤tNode != boundary) return lastVisible; // skip position in unrendered or invisible node - RenderObject* renderer = currentNode->renderer(); + RenderObject* renderer = currentNode.renderer(); if (!renderer || renderer->style().visibility() != VISIBLE) continue; if (rule == CanCrossEditingBoundary && boundaryCrossed) { - lastVisible = currentPos; + lastVisible = currentPosition; break; } // track last visible streamer position - if (isStreamer(currentPos)) - lastVisible = currentPos; + if (isStreamer(currentPosition)) + lastVisible = currentPosition; // Don't move past a position that is visually distinct. We could rely on code above to terminate and - // return lastVisible on the next iteration, but we terminate early to avoid doing a nodeIndex() call. - if (endsOfNodeAreVisuallyDistinctPositions(currentNode) && currentPos.atStartOfNode()) + // return lastVisible on the next iteration, but we terminate early to avoid doing a computeNodeIndex() call. + if (endsOfNodeAreVisuallyDistinctPositions(¤tNode) && currentPosition.atStartOfNode()) return lastVisible; // Return position after tables and nodes which have content that can be ignored. - if (editingIgnoresContent(currentNode) || isTableElement(currentNode)) { - if (currentPos.atEndOfNode()) - return positionAfterNode(currentNode); + if (editingIgnoresContent(currentNode) || isRenderedTable(¤tNode)) { + if (currentPosition.atEndOfNode()) + return positionAfterNode(¤tNode); continue; } // return current position if it is in rendered text - if (renderer->isText()) { - auto& textRenderer = toRenderText(*renderer); + if (is<RenderText>(*renderer)) { + auto& textRenderer = downcast<RenderText>(*renderer); textRenderer.ensureLineBoxes(); if (!textRenderer.firstTextBox()) continue; - if (currentNode != startNode) { + if (¤tNode != startNode) { // This assertion fires in layout tests in the case-transform.html test because // of a mix-up between offsets in the text in the DOM tree with text in the // render tree which can have a different length due to case transformation. // Until we resolve that, disable this so we can run the layout tests! //ASSERT(currentOffset >= renderer->caretMaxOffset()); - return createLegacyEditingPosition(currentNode, renderer->caretMaxOffset()); + return createLegacyEditingPosition(¤tNode, renderer->caretMaxOffset()); } - unsigned textOffset = currentPos.offsetInLeafNode(); + unsigned textOffset = currentPosition.offsetInLeafNode(); auto lastTextBox = textRenderer.lastTextBox(); - for (auto box = textRenderer.firstTextBox(); box; box = box->nextTextBox()) { + for (auto* box = textRenderer.firstTextBox(); box; box = box->nextTextBox()) { if (textOffset <= box->start() + box->len()) { if (textOffset > box->start()) - return currentPos; + return currentPosition; continue; } @@ -677,7 +738,7 @@ Position Position::upstream(EditingBoundaryCrossingRule rule) const otherBox = otherBox->nextLeafChild(); if (!otherBox) break; - if (otherBox == lastTextBox || (&otherBox->renderer() == &textRenderer && toInlineTextBox(otherBox)->start() > textOffset)) + if (otherBox == lastTextBox || (&otherBox->renderer() == &textRenderer && downcast<InlineTextBox>(*otherBox).start() > textOffset)) continuesOnNextLine = false; } @@ -686,12 +747,12 @@ Position Position::upstream(EditingBoundaryCrossingRule rule) const otherBox = otherBox->prevLeafChild(); if (!otherBox) break; - if (otherBox == lastTextBox || (&otherBox->renderer() == &textRenderer && toInlineTextBox(otherBox)->start() > textOffset)) + if (otherBox == lastTextBox || (&otherBox->renderer() == &textRenderer && downcast<InlineTextBox>(*otherBox).start() > textOffset)) continuesOnNextLine = false; } if (continuesOnNextLine) - return currentPos; + return currentPosition; } } } @@ -710,86 +771,86 @@ Position Position::downstream(EditingBoundaryCrossingRule rule) const { Node* startNode = deprecatedNode(); if (!startNode) - return Position(); + return { }; // iterate forward from there, looking for a qualified position Node* boundary = enclosingVisualBoundary(startNode); // FIXME: PositionIterator should respect Before and After positions. - PositionIterator lastVisible = m_anchorType == PositionIsAfterAnchor ? createLegacyEditingPosition(m_anchorNode.get(), caretMaxOffset(m_anchorNode.get())) : *this; - PositionIterator currentPos = lastVisible; + PositionIterator lastVisible = m_anchorType == PositionIsAfterAnchor ? createLegacyEditingPosition(m_anchorNode.get(), caretMaxOffset(*m_anchorNode)) : *this; + PositionIterator currentPosition = lastVisible; bool startEditable = startNode->hasEditableStyle(); Node* lastNode = startNode; bool boundaryCrossed = false; - for (; !currentPos.atEnd(); currentPos.increment()) { - Node* currentNode = currentPos.node(); + for (; !currentPosition.atEnd(); currentPosition.increment()) { + auto& currentNode = *currentPosition.node(); // Don't check for an editability change if we haven't moved to a different node, // to avoid the expense of computing hasEditableStyle(). - if (currentNode != lastNode) { + if (¤tNode != lastNode) { // Don't change editability. - bool currentEditable = currentNode->hasEditableStyle(); + bool currentEditable = currentNode.hasEditableStyle(); if (startEditable != currentEditable) { if (rule == CannotCrossEditingBoundary) break; boundaryCrossed = true; } - lastNode = currentNode; + lastNode = ¤tNode; } // stop before going above the body, up into the head // return the last visible streamer position - if (currentNode->hasTagName(bodyTag) && currentPos.atEndOfNode()) + if (is<HTMLBodyElement>(currentNode) && currentPosition.atEndOfNode()) break; - + // Do not move to a visually distinct position. - if (endsOfNodeAreVisuallyDistinctPositions(currentNode) && currentNode != boundary) + if (endsOfNodeAreVisuallyDistinctPositions(¤tNode) && ¤tNode != boundary) return lastVisible; // Do not move past a visually disinct position. // Note: The first position after the last in a node whose ends are visually distinct - // positions will be [boundary->parentNode(), originalBlock->nodeIndex() + 1]. - if (boundary && boundary->parentNode() == currentNode) + // positions will be [boundary->parentNode(), originalBlock->computeNodeIndex() + 1]. + if (boundary && boundary->parentNode() == ¤tNode) return lastVisible; // skip position in unrendered or invisible node - RenderObject* renderer = currentNode->renderer(); + auto* renderer = currentNode.renderer(); if (!renderer || renderer->style().visibility() != VISIBLE) continue; if (rule == CanCrossEditingBoundary && boundaryCrossed) { - lastVisible = currentPos; + lastVisible = currentPosition; break; } // track last visible streamer position - if (isStreamer(currentPos)) - lastVisible = currentPos; + if (isStreamer(currentPosition)) + lastVisible = currentPosition; // Return position before tables and nodes which have content that can be ignored. - if (editingIgnoresContent(currentNode) || isTableElement(currentNode)) { - if (currentPos.offsetInLeafNode() <= renderer->caretMinOffset()) - return createLegacyEditingPosition(currentNode, renderer->caretMinOffset()); + if (editingIgnoresContent(currentNode) || isRenderedTable(¤tNode)) { + if (currentPosition.atStartOfNode()) + return positionBeforeNode(¤tNode); continue; } // return current position if it is in rendered text - if (renderer->isText()) { - auto& textRenderer = toRenderText(*renderer); + if (is<RenderText>(*renderer)) { + auto& textRenderer = downcast<RenderText>(*renderer); textRenderer.ensureLineBoxes(); if (!textRenderer.firstTextBox()) continue; - if (currentNode != startNode) { - ASSERT(currentPos.atStartOfNode()); - return createLegacyEditingPosition(currentNode, renderer->caretMinOffset()); + if (¤tNode != startNode) { + ASSERT(currentPosition.atStartOfNode()); + return createLegacyEditingPosition(¤tNode, renderer->caretMinOffset()); } - unsigned textOffset = currentPos.offsetInLeafNode(); + unsigned textOffset = currentPosition.offsetInLeafNode(); auto lastTextBox = textRenderer.lastTextBox(); - for (auto box = textRenderer.firstTextBox(); box; box = box->nextTextBox()) { + for (auto* box = textRenderer.firstTextBox(); box; box = box->nextTextBox()) { if (textOffset <= box->end()) { if (textOffset >= box->start()) - return currentPos; + return currentPosition; continue; } @@ -805,7 +866,7 @@ Position Position::downstream(EditingBoundaryCrossingRule rule) const otherBox = otherBox->nextLeafChild(); if (!otherBox) break; - if (otherBox == lastTextBox || (&otherBox->renderer() == &textRenderer && toInlineTextBox(otherBox)->start() >= textOffset)) + if (otherBox == lastTextBox || (&otherBox->renderer() == &textRenderer && downcast<InlineTextBox>(*otherBox).start() >= textOffset)) continuesOnNextLine = false; } @@ -814,12 +875,12 @@ Position Position::downstream(EditingBoundaryCrossingRule rule) const otherBox = otherBox->prevLeafChild(); if (!otherBox) break; - if (otherBox == lastTextBox || (&otherBox->renderer() == &textRenderer && toInlineTextBox(otherBox)->start() >= textOffset)) + if (otherBox == lastTextBox || (&otherBox->renderer() == &textRenderer && downcast<InlineTextBox>(*otherBox).start() >= textOffset)) continuesOnNextLine = false; } if (continuesOnNextLine) - return currentPos; + return currentPosition; } } } @@ -827,6 +888,30 @@ Position Position::downstream(EditingBoundaryCrossingRule rule) const return lastVisible; } +unsigned Position::positionCountBetweenPositions(const Position& a, const Position& b) +{ + if (a.isNull() || b.isNull()) + return UINT_MAX; + + Position endPos; + Position pos; + if (a > b) { + endPos = a; + pos = b; + } else if (a < b) { + endPos = b; + pos = a; + } else + return 0; + + unsigned posCount = 0; + while (!pos.atEndOfTree() && pos != endPos) { + pos = pos.next(); + ++posCount; + } + return posCount; +} + static int boundingBoxLogicalHeight(RenderObject *o, const IntRect &rect) { return o->style().isHorizontalWritingMode() ? rect.height() : rect.width(); @@ -838,23 +923,23 @@ bool Position::hasRenderedNonAnonymousDescendantsWithHeight(const RenderElement& for (RenderObject* o = renderer.firstChild(); o && o != stop; o = o->nextInPreOrder()) { if (!o->nonPseudoNode()) continue; - if (o->isText()) { - if (boundingBoxLogicalHeight(o, toRenderText(o)->linesBoundingBox())) + if (is<RenderText>(*o)) { + if (boundingBoxLogicalHeight(o, downcast<RenderText>(*o).linesBoundingBox())) return true; continue; } - if (o->isLineBreak()) { - if (boundingBoxLogicalHeight(o, toRenderLineBreak(o)->linesBoundingBox())) + if (is<RenderLineBreak>(*o)) { + if (boundingBoxLogicalHeight(o, downcast<RenderLineBreak>(*o).linesBoundingBox())) return true; continue; } - if (o->isBox()) { - if (toRenderBox(o)->pixelSnappedLogicalHeight()) + if (is<RenderBox>(*o)) { + if (roundToInt(downcast<RenderBox>(*o).logicalHeight())) return true; continue; } - if (o->isRenderInline()) { - const RenderInline& renderInline = toRenderInline(*o); + if (is<RenderInline>(*o)) { + const RenderInline& renderInline = downcast<RenderInline>(*o); if (isEmptyInline(renderInline) && boundingBoxLogicalHeight(o, renderInline.linesBoundingBox())) return true; continue; @@ -868,18 +953,6 @@ bool Position::nodeIsUserSelectNone(Node* node) return node && node->renderer() && node->renderer()->style().userSelect() == SELECT_NONE; } -ContainerNode* Position::findParent(const Node* node) -{ - // FIXME: See http://web.ug/82697 - -#if ENABLE(SHADOW_DOM) - if (RuntimeEnabledFeatures::sharedFeatures().shadowDOMEnabled()) - return node->parentNode(); -#endif - - return node->nonShadowBoundaryParentNode(); -} - #if ENABLE(USERSELECT_ALL) bool Position::nodeIsUserSelectAll(const Node* node) { @@ -889,7 +962,7 @@ bool Position::nodeIsUserSelectAll(const Node* node) Node* Position::rootUserSelectAllForNode(Node* node) { if (!node || !nodeIsUserSelectAll(node)) - return 0; + return nullptr; Node* parent = node->parentNode(); if (!parent) return node; @@ -913,52 +986,53 @@ bool Position::isCandidate() const { if (isNull()) return false; - - RenderObject* renderer = deprecatedNode()->renderer(); + + auto* renderer = deprecatedNode()->renderer(); if (!renderer) return false; - + if (renderer->style().visibility() != VISIBLE) return false; - if (renderer->isBR()) + if (renderer->isBR()) { // FIXME: The condition should be m_anchorType == PositionIsBeforeAnchor, but for now we still need to support legacy positions. return !m_offset && m_anchorType != PositionIsAfterAnchor && !nodeIsUserSelectNone(deprecatedNode()->parentNode()); + } - if (renderer->isText()) - return !nodeIsUserSelectNone(deprecatedNode()) && toRenderText(renderer)->containsCaretOffset(m_offset); + if (is<RenderText>(*renderer)) + return !nodeIsUserSelectNone(deprecatedNode()) && downcast<RenderText>(*renderer).containsCaretOffset(m_offset); - if (isTableElement(deprecatedNode()) || editingIgnoresContent(deprecatedNode())) - return (atFirstEditingPositionForNode() || atLastEditingPositionForNode()) && !nodeIsUserSelectNone(deprecatedNode()->parentNode()); + if (positionBeforeOrAfterNodeIsCandidate(*deprecatedNode())) { + return ((atFirstEditingPositionForNode() && m_anchorType == PositionIsBeforeAnchor) + || (atLastEditingPositionForNode() && m_anchorType == PositionIsAfterAnchor)) + && !nodeIsUserSelectNone(deprecatedNode()->parentNode()); + } - if (m_anchorNode->hasTagName(htmlTag)) + if (is<HTMLHtmlElement>(*m_anchorNode)) return false; - - if (isRendererReplacedElement(renderer)) - return !nodeIsUserSelectNone(deprecatedNode()) && atFirstEditingPositionForNode(); - if (renderer->isRenderBlockFlow()) { - RenderBlock& block = toRenderBlock(*renderer); - if (block.logicalHeight() || m_anchorNode->hasTagName(bodyTag)) { + if (is<RenderBlockFlow>(*renderer) || is<RenderGrid>(*renderer) || is<RenderFlexibleBox>(*renderer)) { + RenderBlock& block = downcast<RenderBlock>(*renderer); + if (block.logicalHeight() || is<HTMLBodyElement>(*m_anchorNode)) { if (!Position::hasRenderedNonAnonymousDescendantsWithHeight(block)) return atFirstEditingPositionForNode() && !Position::nodeIsUserSelectNone(deprecatedNode()); return m_anchorNode->hasEditableStyle() && !Position::nodeIsUserSelectNone(deprecatedNode()) && atEditingBoundary(); } - } else - return m_anchorNode->hasEditableStyle() && !Position::nodeIsUserSelectNone(deprecatedNode()) && atEditingBoundary(); + return false; + } - return false; + return m_anchorNode->hasEditableStyle() && !Position::nodeIsUserSelectNone(deprecatedNode()) && atEditingBoundary(); } bool Position::isRenderedCharacter() const { - if (isNull() || !deprecatedNode()->isTextNode()) + if (!is<Text>(deprecatedNode())) return false; - - RenderText* renderer = toText(deprecatedNode())->renderer(); + + RenderText* renderer = downcast<Text>(*deprecatedNode()).renderer(); if (!renderer) return false; - + return renderer->containsRenderedCharacterOffset(m_offset); } @@ -967,69 +1041,68 @@ static bool inSameEnclosingBlockFlowElement(Node* a, Node* b) return a && b && deprecatedEnclosingBlockFlowElement(a) == deprecatedEnclosingBlockFlowElement(b); } -bool Position::rendersInDifferentPosition(const Position &pos) const +bool Position::rendersInDifferentPosition(const Position& position) const { - if (isNull() || pos.isNull()) + if (isNull() || position.isNull()) return false; - RenderObject* renderer = deprecatedNode()->renderer(); + auto* renderer = deprecatedNode()->renderer(); if (!renderer) return false; - RenderObject* posRenderer = pos.deprecatedNode()->renderer(); - if (!posRenderer) + auto* positionRenderer = position.deprecatedNode()->renderer(); + if (!positionRenderer) return false; - if (renderer->style().visibility() != VISIBLE || - posRenderer->style().visibility() != VISIBLE) + if (renderer->style().visibility() != VISIBLE || positionRenderer->style().visibility() != VISIBLE) return false; - if (deprecatedNode() == pos.deprecatedNode()) { - if (deprecatedNode()->hasTagName(brTag)) + if (deprecatedNode() == position.deprecatedNode()) { + if (is<HTMLBRElement>(*deprecatedNode())) return false; - if (m_offset == pos.deprecatedEditingOffset()) + if (m_offset == position.deprecatedEditingOffset()) return false; - - if (!deprecatedNode()->isTextNode() && !pos.deprecatedNode()->isTextNode()) { - if (m_offset != pos.deprecatedEditingOffset()) + + if (!is<Text>(*deprecatedNode()) && !is<Text>(*position.deprecatedNode())) { + if (m_offset != position.deprecatedEditingOffset()) return true; } } - - if (deprecatedNode()->hasTagName(brTag) && pos.isCandidate()) + + if (is<HTMLBRElement>(*deprecatedNode()) && position.isCandidate()) return true; - - if (pos.deprecatedNode()->hasTagName(brTag) && isCandidate()) + + if (is<HTMLBRElement>(*position.deprecatedNode()) && isCandidate()) return true; - - if (!inSameEnclosingBlockFlowElement(deprecatedNode(), pos.deprecatedNode())) + + if (!inSameEnclosingBlockFlowElement(deprecatedNode(), position.deprecatedNode())) return true; - if (renderer->isText() && !toRenderText(renderer)->containsCaretOffset(m_offset)) + if (is<RenderText>(*renderer) && !downcast<RenderText>(*renderer).containsCaretOffset(m_offset)) return false; - if (posRenderer->isText() && !toRenderText(posRenderer)->containsCaretOffset(pos.m_offset)) + if (is<RenderText>(*positionRenderer) && !downcast<RenderText>(*positionRenderer).containsCaretOffset(position.m_offset)) return false; - int thisRenderedOffset = renderer->isText() ? toRenderText(renderer)->countRenderedCharacterOffsetsUntil(m_offset) : m_offset; - int posRenderedOffset = posRenderer->isText() ? toRenderText(posRenderer)->countRenderedCharacterOffsetsUntil(pos.m_offset) : pos.m_offset; + int thisRenderedOffset = is<RenderText>(*renderer) ? downcast<RenderText>(*renderer).countRenderedCharacterOffsetsUntil(m_offset) : m_offset; + int positionRenderedOffset = is<RenderText>(*positionRenderer) ? downcast<RenderText>(*positionRenderer).countRenderedCharacterOffsetsUntil(position.m_offset) : position.m_offset; - if (renderer == posRenderer && thisRenderedOffset == posRenderedOffset) + if (renderer == positionRenderer && thisRenderedOffset == positionRenderedOffset) return false; int ignoredCaretOffset; InlineBox* b1; getInlineBoxAndOffset(DOWNSTREAM, b1, ignoredCaretOffset); InlineBox* b2; - pos.getInlineBoxAndOffset(DOWNSTREAM, b2, ignoredCaretOffset); + position.getInlineBoxAndOffset(DOWNSTREAM, b2, ignoredCaretOffset); LOG(Editing, "renderer: %p [%p]\n", renderer, b1); LOG(Editing, "thisRenderedOffset: %d\n", thisRenderedOffset); - LOG(Editing, "posRenderer: %p [%p]\n", posRenderer, b2); - LOG(Editing, "posRenderedOffset: %d\n", posRenderedOffset); - LOG(Editing, "node min/max: %d:%d\n", caretMinOffset(deprecatedNode()), caretMaxOffset(deprecatedNode())); - LOG(Editing, "pos node min/max: %d:%d\n", caretMinOffset(pos.deprecatedNode()), caretMaxOffset(pos.deprecatedNode())); + LOG(Editing, "posRenderer: %p [%p]\n", positionRenderer, b2); + LOG(Editing, "posRenderedOffset: %d\n", positionRenderedOffset); + LOG(Editing, "node min/max: %d:%d\n", caretMinOffset(*deprecatedNode()), caretMaxOffset(*deprecatedNode())); + LOG(Editing, "pos node min/max: %d:%d\n", caretMinOffset(*position.deprecatedNode()), caretMaxOffset(*position.deprecatedNode())); LOG(Editing, "----------------------------------------------------------------------\n"); if (!b1 || !b2) { @@ -1040,13 +1113,13 @@ bool Position::rendersInDifferentPosition(const Position &pos) const return true; } - if (nextRenderedEditable(deprecatedNode()) == pos.deprecatedNode() - && thisRenderedOffset == caretMaxOffset(deprecatedNode()) && !posRenderedOffset) { + if (nextRenderedEditable(deprecatedNode()) == position.deprecatedNode() + && thisRenderedOffset == caretMaxOffset(*deprecatedNode()) && !positionRenderedOffset) { return false; } - if (previousRenderedEditable(deprecatedNode()) == pos.deprecatedNode() - && !thisRenderedOffset && posRenderedOffset == caretMaxOffset(pos.deprecatedNode())) { + if (previousRenderedEditable(deprecatedNode()) == position.deprecatedNode() + && !thisRenderedOffset && positionRenderedOffset == caretMaxOffset(*position.deprecatedNode())) { return false; } @@ -1058,21 +1131,21 @@ Position Position::leadingWhitespacePosition(EAffinity affinity, bool considerNo { ASSERT(isEditablePosition(*this)); if (isNull()) - return Position(); + return { }; - if (upstream().deprecatedNode()->hasTagName(brTag)) - return Position(); + if (is<HTMLBRElement>(*upstream().deprecatedNode())) + return { }; Position prev = previousCharacterPosition(affinity); - if (prev != *this && inSameEnclosingBlockFlowElement(deprecatedNode(), prev.deprecatedNode()) && prev.deprecatedNode()->isTextNode()) { - String string = toText(prev.deprecatedNode())->data(); + if (prev != *this && inSameEnclosingBlockFlowElement(deprecatedNode(), prev.deprecatedNode()) && is<Text>(*prev.deprecatedNode())) { + String string = downcast<Text>(*prev.deprecatedNode()).data(); UChar c = string[prev.deprecatedEditingOffset()]; - if (considerNonCollapsibleWhitespace ? (isSpaceOrNewline(c) || c == noBreakSpace) : isCollapsibleWhitespace(c)) + if (considerNonCollapsibleWhitespace ? (isSpaceOrNewline(c) || c == noBreakSpace) : deprecatedIsCollapsibleWhitespace(c)) if (isEditablePosition(prev)) return prev; } - return Position(); + return { }; } // This assumes that it starts in editable content. @@ -1080,16 +1153,16 @@ Position Position::trailingWhitespacePosition(EAffinity, bool considerNonCollaps { ASSERT(isEditablePosition(*this)); if (isNull()) - return Position(); + return { }; VisiblePosition v(*this); UChar c = v.characterAfter(); // The space must not be in another paragraph and it must be editable. if (!isEndOfParagraph(v) && v.next(CannotCrossEditingBoundary).isNotNull()) - if (considerNonCollapsibleWhitespace ? (isSpaceOrNewline(c) || c == noBreakSpace) : isCollapsibleWhitespace(c)) + if (considerNonCollapsibleWhitespace ? (isSpaceOrNewline(c) || c == noBreakSpace) : deprecatedIsCollapsibleWhitespace(c)) return *this; - return Position(); + return { }; } void Position::getInlineBoxAndOffset(EAffinity affinity, InlineBox*& inlineBox, int& caretOffset) const @@ -1097,13 +1170,11 @@ void Position::getInlineBoxAndOffset(EAffinity affinity, InlineBox*& inlineBox, getInlineBoxAndOffset(affinity, primaryDirection(), inlineBox, caretOffset); } -static bool isNonTextLeafChild(RenderObject* object) +static bool isNonTextLeafChild(RenderObject& object) { - if (object->isText()) - return false; - if (toRenderElement(object)->firstChild()) + if (is<RenderText>(object)) return false; - return true; + return !downcast<RenderElement>(object).firstChild(); } static InlineTextBox* searchAheadForBetterMatch(RenderObject* renderer) @@ -1111,16 +1182,16 @@ static InlineTextBox* searchAheadForBetterMatch(RenderObject* renderer) RenderBlock* container = renderer->containingBlock(); RenderObject* next = renderer; while ((next = next->nextInPreOrder(container))) { - if (next->isRenderBlock()) - return 0; + if (is<RenderBlock>(*next)) + return nullptr; if (next->isBR()) - return 0; - if (isNonTextLeafChild(next)) - return 0; - if (next->isText()) { - InlineTextBox* match = 0; + return nullptr; + if (isNonTextLeafChild(*next)) + return nullptr; + if (is<RenderText>(*next)) { + InlineTextBox* match = nullptr; int minOffset = INT_MAX; - for (InlineTextBox* box = toRenderText(next)->firstTextBox(); box; box = box->nextTextBox()) { + for (InlineTextBox* box = downcast<RenderText>(*next).firstTextBox(); box; box = box->nextTextBox()) { int caretMinOffset = box->caretMinOffset(); if (caretMinOffset < minOffset) { match = box; @@ -1131,7 +1202,7 @@ static InlineTextBox* searchAheadForBetterMatch(RenderObject* renderer) return match; } } - return 0; + return nullptr; } static Position downstreamIgnoringEditingBoundaries(Position position) @@ -1160,15 +1231,15 @@ void Position::getInlineBoxAndOffset(EAffinity affinity, TextDirection primaryDi RenderObject* renderer = deprecatedNode()->renderer(); if (renderer->isBR()) - inlineBox = !caretOffset ? toRenderLineBreak(renderer)->inlineBoxWrapper() : nullptr; - else if (renderer->isText()) { - auto textRenderer = toRenderText(renderer); - textRenderer->ensureLineBoxes(); + inlineBox = !caretOffset ? downcast<RenderLineBreak>(*renderer).inlineBoxWrapper() : nullptr; + else if (is<RenderText>(*renderer)) { + auto& textRenderer = downcast<RenderText>(*renderer); + textRenderer.ensureLineBoxes(); InlineTextBox* box; - InlineTextBox* candidate = 0; + InlineTextBox* candidate = nullptr; - for (box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { + for (box = textRenderer.firstTextBox(); box; box = box->nextTextBox()) { int caretMinOffset = box->caretMinOffset(); int caretMaxOffset = box->caretMaxOffset(); @@ -1187,15 +1258,15 @@ void Position::getInlineBoxAndOffset(EAffinity affinity, TextDirection primaryDi candidate = box; } - if (candidate && candidate == textRenderer->lastTextBox() && affinity == DOWNSTREAM) { - box = searchAheadForBetterMatch(textRenderer); + if (candidate && candidate == textRenderer.lastTextBox() && affinity == DOWNSTREAM) { + box = searchAheadForBetterMatch(&textRenderer); if (box) caretOffset = box->caretMinOffset(); } inlineBox = box ? box : candidate; } else { - inlineBox = 0; - if (canHaveChildrenForEditing(deprecatedNode()) && renderer->isRenderBlockFlow() && hasRenderedNonAnonymousDescendantsWithHeight(toRenderBlock(*renderer))) { + inlineBox = nullptr; + if (canHaveChildrenForEditing(*deprecatedNode()) && is<RenderBlockFlow>(*renderer) && hasRenderedNonAnonymousDescendantsWithHeight(downcast<RenderBlockFlow>(*renderer))) { // Try a visually equivalent position with possibly opposite editability. This helps in case |this| is in // an editable block but surrounded by non-editable positions. It acts to negate the logic at the beginning // of RenderObject::createVisiblePosition(). @@ -1209,8 +1280,8 @@ void Position::getInlineBoxAndOffset(EAffinity affinity, TextDirection primaryDi equivalent.getInlineBoxAndOffset(UPSTREAM, primaryDirection, inlineBox, caretOffset); return; } - if (renderer->isBox()) { - inlineBox = toRenderBox(renderer)->inlineBoxWrapper(); + if (is<RenderBox>(*renderer)) { + inlineBox = downcast<RenderBox>(*renderer).inlineBoxWrapper(); if (!inlineBox || (caretOffset > inlineBox->caretMinOffset() && caretOffset < inlineBox->caretMaxOffset())) return; } @@ -1310,17 +1381,14 @@ void Position::getInlineBoxAndOffset(EAffinity affinity, TextDirection primaryDi TextDirection Position::primaryDirection() const { - TextDirection primaryDirection = LTR; - for (const RenderObject* r = m_anchorNode->renderer(); r; r = r->parent()) { - if (r->isRenderBlockFlow()) { - primaryDirection = toRenderBlockFlow(r)->style().direction(); - break; - } - } - - return primaryDirection; + if (!m_anchorNode->renderer()) + return LTR; + if (auto* blockFlow = lineageOfType<RenderBlockFlow>(*m_anchorNode->renderer()).first()) + return blockFlow->style().direction(); + return LTR; } +#if ENABLE(TREE_DEBUGGING) void Position::debugPosition(const char* msg) const { @@ -1330,8 +1398,6 @@ void Position::debugPosition(const char* msg) const fprintf(stderr, "Position [%s]: %s [%p] at %d\n", msg, deprecatedNode()->nodeName().utf8().data(), deprecatedNode(), m_offset); } -#ifndef NDEBUG - void Position::formatForDebugger(char* buffer, unsigned length) const { StringBuilder result; @@ -1384,11 +1450,139 @@ void Position::showTreeForThis() const #endif +bool Position::equals(const Position& other) const +{ + if (!m_anchorNode) + return !m_anchorNode == !other.m_anchorNode; + if (!other.m_anchorNode) + return false; + + switch (anchorType()) { + case PositionIsBeforeChildren: + ASSERT(!is<Text>(*m_anchorNode)); + switch (other.anchorType()) { + case PositionIsBeforeChildren: + ASSERT(!is<Text>(*other.m_anchorNode)); + return m_anchorNode == other.m_anchorNode; + case PositionIsAfterChildren: + ASSERT(!is<Text>(*other.m_anchorNode)); + return m_anchorNode == other.m_anchorNode && !m_anchorNode->hasChildNodes(); + case PositionIsOffsetInAnchor: + return m_anchorNode == other.m_anchorNode && !other.m_offset; + case PositionIsBeforeAnchor: + return m_anchorNode->firstChild() == other.m_anchorNode; + case PositionIsAfterAnchor: + return false; + } + break; + case PositionIsAfterChildren: + ASSERT(!is<Text>(*m_anchorNode)); + switch (other.anchorType()) { + case PositionIsBeforeChildren: + ASSERT(!is<Text>(*other.m_anchorNode)); + return m_anchorNode == other.m_anchorNode && !m_anchorNode->hasChildNodes(); + case PositionIsAfterChildren: + ASSERT(!is<Text>(*other.m_anchorNode)); + return m_anchorNode == other.m_anchorNode; + case PositionIsOffsetInAnchor: + return m_anchorNode == other.m_anchorNode && m_anchorNode->countChildNodes() == static_cast<unsigned>(m_offset); + case PositionIsBeforeAnchor: + return false; + case PositionIsAfterAnchor: + return m_anchorNode->lastChild() == other.m_anchorNode; + } + break; + case PositionIsOffsetInAnchor: + switch (other.anchorType()) { + case PositionIsBeforeChildren: + ASSERT(!is<Text>(*other.m_anchorNode)); + return m_anchorNode == other.m_anchorNode && !m_offset; + case PositionIsAfterChildren: + ASSERT(!is<Text>(*other.m_anchorNode)); + return m_anchorNode == other.m_anchorNode && m_offset == static_cast<int>(other.m_anchorNode->countChildNodes()); + case PositionIsOffsetInAnchor: + return m_anchorNode == other.m_anchorNode && m_offset == other.m_offset; + case PositionIsBeforeAnchor: + return m_anchorNode->traverseToChildAt(m_offset) == other.m_anchorNode; + case PositionIsAfterAnchor: + return m_offset && m_anchorNode->traverseToChildAt(m_offset - 1) == other.m_anchorNode; + } + break; + case PositionIsBeforeAnchor: + switch (other.anchorType()) { + case PositionIsBeforeChildren: + ASSERT(!is<Text>(*other.m_anchorNode)); + return m_anchorNode == other.m_anchorNode->firstChild(); + case PositionIsAfterChildren: + ASSERT(!is<Text>(*other.m_anchorNode)); + return false; + case PositionIsOffsetInAnchor: + return m_anchorNode == other.m_anchorNode->traverseToChildAt(other.m_offset); + case PositionIsBeforeAnchor: + return m_anchorNode == other.m_anchorNode; + case PositionIsAfterAnchor: + return m_anchorNode->previousSibling() == other.m_anchorNode; + } + break; + case PositionIsAfterAnchor: + switch (other.anchorType()) { + case PositionIsBeforeChildren: + ASSERT(!is<Text>(*other.m_anchorNode)); + return false; + case PositionIsAfterChildren: + ASSERT(!is<Text>(*other.m_anchorNode)); + return m_anchorNode == other.m_anchorNode->lastChild(); + case PositionIsOffsetInAnchor: + return other.m_offset && m_anchorNode == other.m_anchorNode->traverseToChildAt(other.m_offset - 1); + case PositionIsBeforeAnchor: + return m_anchorNode->nextSibling() == other.m_anchorNode; + case PositionIsAfterAnchor: + return m_anchorNode == other.m_anchorNode; + } + break; + } + + ASSERT_NOT_REACHED(); + return false; +} + +static TextStream& operator<<(TextStream& stream, Position::AnchorType anchorType) +{ + switch (anchorType) { + case Position::PositionIsOffsetInAnchor: + stream << "offset in anchor"; + break; + case Position::PositionIsBeforeAnchor: + stream << "before anchor"; + break; + case Position::PositionIsAfterAnchor: + stream << "after anchor"; + break; + case Position::PositionIsBeforeChildren: + stream << "before children"; + break; + case Position::PositionIsAfterChildren: + stream << "after children"; + break; + } + return stream; +} + +TextStream& operator<<(TextStream& stream, const Position& position) +{ + TextStream::GroupScope scope(stream); + stream << "Position " << &position; + stream.dumpProperty("anchor node", position.anchorNode()); + stream.dumpProperty("offset", position.offsetInContainerNode()); + stream.dumpProperty("anchor type", position.anchorType()); + + return stream; +} } // namespace WebCore -#ifndef NDEBUG +#if ENABLE(TREE_DEBUGGING) void showTree(const WebCore::Position& pos) { diff --git a/Source/WebCore/dom/Position.h b/Source/WebCore/dom/Position.h index e1002d871..f7c42bb8e 100644 --- a/Source/WebCore/dom/Position.h +++ b/Source/WebCore/dom/Position.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -23,15 +23,13 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef Position_h -#define Position_h +#pragma once #include "ContainerNode.h" #include "EditingBoundary.h" #include "TextAffinity.h" -#include "TextDirection.h" +#include "TextFlags.h" #include <wtf/Assertions.h> -#include <wtf/PassRefPtr.h> #include <wtf/RefPtr.h> namespace WebCore { @@ -44,6 +42,7 @@ class Range; class RenderElement; class RenderObject; class Text; +class TextStream; enum PositionMoveType { CodePoint, // Move by a single code point. @@ -62,45 +61,30 @@ public: }; Position() - : m_offset(0) - , m_anchorType(PositionIsOffsetInAnchor) + : m_anchorType(PositionIsOffsetInAnchor) , m_isLegacyEditingPosition(false) { } - // For creating legacy editing positions: (Anchor type will be determined from editingIgnoresContent(node)) - class LegacyEditingOffset { - public: - int value() const { return m_offset; } - - private: - explicit LegacyEditingOffset(int offset) : m_offset(offset) { } - - friend Position createLegacyEditingPosition(PassRefPtr<Node>, int offset); - - int m_offset; - }; - Position(PassRefPtr<Node> anchorNode, LegacyEditingOffset); - // For creating before/after positions: - Position(PassRefPtr<Node> anchorNode, AnchorType); - Position(PassRefPtr<Text> textNode, unsigned offset); + WEBCORE_EXPORT Position(Node* anchorNode, AnchorType); + Position(Text* textNode, unsigned offset); // For creating offset positions: // FIXME: This constructor should eventually go away. See bug 63040. - Position(PassRefPtr<Node> anchorNode, int offset, AnchorType); + WEBCORE_EXPORT Position(Node* anchorNode, int offset, AnchorType); AnchorType anchorType() const { return static_cast<AnchorType>(m_anchorType); } - void clear() { m_anchorNode.clear(); m_offset = 0; m_anchorType = PositionIsOffsetInAnchor; m_isLegacyEditingPosition = false; } + void clear() { m_anchorNode = nullptr; m_offset = 0; m_anchorType = PositionIsOffsetInAnchor; m_isLegacyEditingPosition = false; } // These are always DOM compliant values. Editing positions like [img, 0] (aka [img, before]) - // will return img->parentNode() and img->nodeIndex() from these functions. - Node* containerNode() const; // NULL for a before/after position anchored to a node with no parent + // will return img->parentNode() and img->computeNodeIndex() from these functions. + WEBCORE_EXPORT Node* containerNode() const; // null for a before/after position anchored to a node with no parent Text* containerText() const; int computeOffsetInContainerNode() const; // O(n) for before/after-anchored positions, O(1) for parent-anchored positions - Position parentAnchoredEquivalent() const; // Convenience method for DOM positions that also fixes up some positions for editing + WEBCORE_EXPORT Position parentAnchoredEquivalent() const; // Convenience method for DOM positions that also fixes up some positions for editing // Inline O(1) access for Positions which callers know to be parent-anchored int offsetInContainerNode() const @@ -128,29 +112,29 @@ public: // will be treated as before ignoredNode (thus node() is really after the position, not containing it). Node* deprecatedNode() const { return m_anchorNode.get(); } - Document* document() const { return m_anchorNode ? &m_anchorNode->document() : 0; } + Document* document() const { return m_anchorNode ? &m_anchorNode->document() : nullptr; } Element* rootEditableElement() const { Node* container = containerNode(); - return container ? container->rootEditableElement() : 0; + return container ? container->rootEditableElement() : nullptr; } // These should only be used for PositionIsOffsetInAnchor positions, unless // the position is a legacy editing position. - void moveToPosition(PassRefPtr<Node> anchorNode, int offset); + void moveToPosition(Node* anchorNode, int offset); void moveToOffset(int offset); bool isNull() const { return !m_anchorNode; } bool isNotNull() const { return m_anchorNode; } - bool isOrphan() const { return m_anchorNode && !m_anchorNode->inDocument(); } + bool isOrphan() const { return m_anchorNode && !m_anchorNode->isConnected(); } Element* element() const; // Move up or down the DOM by one position. // Offsets are computed using render text for nodes that have renderers - but note that even when // using composed characters, the result may be inside a single user-visible character if a ligature is formed. - Position previous(PositionMoveType = CodePoint) const; - Position next(PositionMoveType = CodePoint) const; + WEBCORE_EXPORT Position previous(PositionMoveType = CodePoint) const; + WEBCORE_EXPORT Position next(PositionMoveType = CodePoint) const; static int uncheckedPreviousOffset(const Node*, int current); static int uncheckedPreviousOffsetForBackwardDeletion(const Node*, int current); static int uncheckedNextOffset(const Node*, int current); @@ -170,12 +154,12 @@ public: // FIXME: Make these non-member functions and put them somewhere in the editing directory. // These aren't really basic "position" operations. More high level editing helper functions. - Position leadingWhitespacePosition(EAffinity, bool considerNonCollapsibleWhitespace = false) const; - Position trailingWhitespacePosition(EAffinity, bool considerNonCollapsibleWhitespace = false) const; + WEBCORE_EXPORT Position leadingWhitespacePosition(EAffinity, bool considerNonCollapsibleWhitespace = false) const; + WEBCORE_EXPORT Position trailingWhitespacePosition(EAffinity, bool considerNonCollapsibleWhitespace = false) const; // These return useful visually equivalent positions. - Position upstream(EditingBoundaryCrossingRule = CannotCrossEditingBoundary) const; - Position downstream(EditingBoundaryCrossingRule = CannotCrossEditingBoundary) const; + WEBCORE_EXPORT Position upstream(EditingBoundaryCrossingRule = CannotCrossEditingBoundary) const; + WEBCORE_EXPORT Position downstream(EditingBoundaryCrossingRule = CannotCrossEditingBoundary) const; bool isCandidate() const; bool isRenderedCharacter() const; @@ -186,6 +170,9 @@ public: TextDirection primaryDirection() const; + // Returns the number of positions that exist between two positions. + static unsigned positionCountBetweenPositions(const Position&, const Position&); + static bool hasRenderedNonAnonymousDescendantsWithHeight(const RenderElement&); static bool nodeIsUserSelectNone(Node*); #if ENABLE(USERSELECT_ALL) @@ -195,18 +182,26 @@ public: static bool nodeIsUserSelectAll(const Node*) { return false; } static Node* rootUserSelectAllForNode(Node*) { return 0; } #endif - static ContainerNode* findParent(const Node*); - + void debugPosition(const char* msg = "") const; -#ifndef NDEBUG +#if ENABLE(TREE_DEBUGGING) void formatForDebugger(char* buffer, unsigned length) const; void showAnchorTypeAndOffset() const; void showTreeForThis() const; #endif - + + // This is a tentative enhancement of operator== to account for different position types. + // FIXME: Combine this function with operator== + bool equals(const Position&) const; + private: - int offsetForPositionAfterAnchor() const; + // For creating legacy editing positions: (Anchor type will be determined from editingIgnoresContent(node)) + enum class LegacyEditingPositionFlag { On }; + WEBCORE_EXPORT Position(Node* anchorNode, unsigned offset, LegacyEditingPositionFlag); + friend Position createLegacyEditingPosition(Node*, unsigned offset); + + WEBCORE_EXPORT int offsetForPositionAfterAnchor() const; Position previousCharacterPosition(EAffinity) const; Position nextCharacterPosition(EAffinity) const; @@ -217,14 +212,14 @@ private: // m_offset can be the offset inside m_anchorNode, or if editingIgnoresContent(m_anchorNode) // returns true, then other places in editing will treat m_offset == 0 as "before the anchor" // and m_offset > 0 as "after the anchor node". See parentAnchoredEquivalent for more info. - int m_offset; + int m_offset { 0 }; unsigned m_anchorType : 3; bool m_isLegacyEditingPosition : 1; }; -inline Position createLegacyEditingPosition(PassRefPtr<Node> node, int offset) +inline Position createLegacyEditingPosition(Node* node, unsigned offset) { - return Position(node, Position::LegacyEditingOffset(offset)); + return { node, offset, Position::LegacyEditingPositionFlag::On }; } inline bool operator==(const Position& a, const Position& b) @@ -245,7 +240,7 @@ inline bool operator<(const Position& a, const Position& b) return false; if (a.anchorNode() == b.anchorNode()) return a.deprecatedEditingOffset() < b.deprecatedEditingOffset(); - return b.anchorNode()->compareDocumentPosition(a.anchorNode()) == Node::DOCUMENT_POSITION_PRECEDING; + return b.anchorNode()->compareDocumentPosition(*a.anchorNode()) == Node::DOCUMENT_POSITION_PRECEDING; } inline bool operator>(const Position& a, const Position& b) @@ -263,23 +258,16 @@ inline bool operator<=(const Position& a, const Position& b) return !a.isNull() && !b.isNull() && (a == b || a < b); } -// We define position creation functions to make callsites more readable. -// These are inline to prevent ref-churn when returning a Position object. -// If we ever add a PassPosition we can make these non-inline. - inline Position positionInParentBeforeNode(const Node* node) { - // FIXME: This should ASSERT(node->parentNode()) - // At least one caller currently hits this ASSERT though, which indicates - // that the caller is trying to make a position relative to a disconnected node (which is likely an error) - // Specifically, editing/deleting/delete-ligature-001.html crashes with ASSERT(node->parentNode()) - return Position(Position::findParent(node), node->nodeIndex(), Position::PositionIsOffsetInAnchor); + ASSERT(node->parentNode()); + return Position(node->parentNode(), node->computeNodeIndex(), Position::PositionIsOffsetInAnchor); } inline Position positionInParentAfterNode(const Node* node) { - ASSERT(Position::findParent(node)); - return Position(Position::findParent(node), node->nodeIndex() + 1, Position::PositionIsOffsetInAnchor); + ASSERT(node->parentNode()); + return Position(node->parentNode(), node->computeNodeIndex() + 1, Position::PositionIsOffsetInAnchor); } // positionBeforeNode and positionAfterNode return neighbor-anchored positions, construction is O(1) @@ -297,10 +285,10 @@ inline Position positionAfterNode(Node* anchorNode) inline int lastOffsetInNode(Node* node) { - return node->offsetInCharacters() ? node->maxCharacterOffset() : static_cast<int>(node->childNodeCount()); + return node->offsetInCharacters() ? node->maxCharacterOffset() : static_cast<int>(node->countChildNodes()); } -// firstPositionInNode and lastPositionInNode return parent-anchored positions, lastPositionInNode construction is O(n) due to childNodeCount() +// firstPositionInNode and lastPositionInNode return parent-anchored positions, lastPositionInNode construction is O(n) due to countChildNodes() inline Position firstPositionInNode(Node* anchorNode) { if (anchorNode->isTextNode()) @@ -340,12 +328,12 @@ inline bool offsetIsBeforeLastNodeOffset(int offset, Node* anchorNode) return offset < currentOffset; } +TextStream& operator<<(TextStream&, const Position&); + } // namespace WebCore -#ifndef NDEBUG -// Outside the WebCore namespace for ease of invocation from gdb. +#if ENABLE(TREE_DEBUGGING) +// Outside the WebCore namespace for ease of invocation from the debugger. void showTree(const WebCore::Position&); void showTree(const WebCore::Position*); #endif - -#endif // Position_h diff --git a/Source/WebCore/dom/PositionIterator.cpp b/Source/WebCore/dom/PositionIterator.cpp index 5e7ff75e0..c1b0a0c54 100644 --- a/Source/WebCore/dom/PositionIterator.cpp +++ b/Source/WebCore/dom/PositionIterator.cpp @@ -26,10 +26,12 @@ #include "config.h" #include "PositionIterator.h" +#include "HTMLBodyElement.h" +#include "HTMLElement.h" +#include "HTMLHtmlElement.h" #include "HTMLNames.h" -#include "RenderBlock.h" +#include "RenderBlockFlow.h" #include "RenderText.h" -#include "TextIterator.h" #include "htmlediting.h" namespace WebCore { @@ -41,10 +43,12 @@ PositionIterator::operator Position() const if (m_nodeAfterPositionInAnchor) { ASSERT(m_nodeAfterPositionInAnchor->parentNode() == m_anchorNode); // FIXME: This check is inadaquete because any ancestor could be ignored by editing - if (editingIgnoresContent(m_nodeAfterPositionInAnchor->parentNode())) + if (positionBeforeOrAfterNodeIsCandidate(*m_anchorNode)) return positionBeforeNode(m_anchorNode); return positionInParentBeforeNode(m_nodeAfterPositionInAnchor); } + if (positionBeforeOrAfterNodeIsCandidate(*m_anchorNode)) + return atStartOfNode() ? positionBeforeNode(m_anchorNode) : positionAfterNode(m_anchorNode); if (m_anchorNode->hasChildNodes()) return lastPositionInOrAfterNode(m_anchorNode); return createLegacyEditingPosition(m_anchorNode, m_offsetInAnchor); @@ -62,7 +66,7 @@ void PositionIterator::increment() return; } - if (!m_anchorNode->hasChildNodes() && m_offsetInAnchor < lastOffsetForEditing(m_anchorNode)) + if (m_anchorNode->renderer() && !m_anchorNode->hasChildNodes() && m_offsetInAnchor < lastOffsetForEditing(*m_anchorNode)) m_offsetInAnchor = Position::uncheckedNextOffset(m_anchorNode, m_offsetInAnchor); else { m_nodeAfterPositionInAnchor = m_anchorNode; @@ -80,8 +84,8 @@ void PositionIterator::decrement() if (m_nodeAfterPositionInAnchor) { m_anchorNode = m_nodeAfterPositionInAnchor->previousSibling(); if (m_anchorNode) { - m_nodeAfterPositionInAnchor = 0; - m_offsetInAnchor = m_anchorNode->hasChildNodes() ? 0 : lastOffsetForEditing(m_anchorNode); + m_nodeAfterPositionInAnchor = nullptr; + m_offsetInAnchor = m_anchorNode->hasChildNodes() ? 0 : lastOffsetForEditing(*m_anchorNode); } else { m_nodeAfterPositionInAnchor = m_nodeAfterPositionInAnchor->parentNode(); m_anchorNode = m_nodeAfterPositionInAnchor->parentNode(); @@ -92,9 +96,9 @@ void PositionIterator::decrement() if (m_anchorNode->hasChildNodes()) { m_anchorNode = m_anchorNode->lastChild(); - m_offsetInAnchor = m_anchorNode->hasChildNodes()? 0: lastOffsetForEditing(m_anchorNode); + m_offsetInAnchor = m_anchorNode->hasChildNodes()? 0: lastOffsetForEditing(*m_anchorNode); } else { - if (m_offsetInAnchor) + if (m_offsetInAnchor && m_anchorNode->renderer()) m_offsetInAnchor = Position::uncheckedPreviousOffset(m_anchorNode, m_offsetInAnchor); else { m_nodeAfterPositionInAnchor = m_anchorNode; @@ -118,7 +122,7 @@ bool PositionIterator::atEnd() const return true; if (m_nodeAfterPositionInAnchor) return false; - return !m_anchorNode->parentNode() && (m_anchorNode->hasChildNodes() || m_offsetInAnchor >= lastOffsetForEditing(m_anchorNode)); + return !m_anchorNode->parentNode() && (m_anchorNode->hasChildNodes() || m_offsetInAnchor >= lastOffsetForEditing(*m_anchorNode)); } bool PositionIterator::atStartOfNode() const @@ -136,7 +140,7 @@ bool PositionIterator::atEndOfNode() const return true; if (m_nodeAfterPositionInAnchor) return false; - return m_anchorNode->hasChildNodes() || m_offsetInAnchor >= lastOffsetForEditing(m_anchorNode); + return m_anchorNode->hasChildNodes() || m_offsetInAnchor >= lastOffsetForEditing(*m_anchorNode); } bool PositionIterator::isCandidate() const @@ -154,15 +158,15 @@ bool PositionIterator::isCandidate() const if (renderer->isBR()) return !m_offsetInAnchor && !Position::nodeIsUserSelectNone(m_anchorNode->parentNode()); - if (renderer->isText()) - return !Position::nodeIsUserSelectNone(m_anchorNode) && toRenderText(renderer)->containsCaretOffset(m_offsetInAnchor); + if (is<RenderText>(*renderer)) + return !Position::nodeIsUserSelectNone(m_anchorNode) && downcast<RenderText>(*renderer).containsCaretOffset(m_offsetInAnchor); - if (isTableElement(m_anchorNode) || editingIgnoresContent(m_anchorNode)) + if (isRenderedTable(m_anchorNode) || editingIgnoresContent(*m_anchorNode)) return (atStartOfNode() || atEndOfNode()) && !Position::nodeIsUserSelectNone(m_anchorNode->parentNode()); - if (!m_anchorNode->hasTagName(htmlTag) && renderer->isRenderBlockFlow()) { - RenderBlock& block = toRenderBlock(*renderer); - if (block.logicalHeight() || m_anchorNode->hasTagName(bodyTag)) { + if (!is<HTMLHtmlElement>(*m_anchorNode) && is<RenderBlockFlow>(*renderer)) { + RenderBlockFlow& block = downcast<RenderBlockFlow>(*renderer); + if (block.logicalHeight() || is<HTMLBodyElement>(*m_anchorNode)) { if (!Position::hasRenderedNonAnonymousDescendantsWithHeight(block)) return atStartOfNode() && !Position::nodeIsUserSelectNone(m_anchorNode); return m_anchorNode->hasEditableStyle() && !Position::nodeIsUserSelectNone(m_anchorNode) && Position(*this).atEditingBoundary(); diff --git a/Source/WebCore/dom/PositionIterator.h b/Source/WebCore/dom/PositionIterator.h index 7af897759..3c4a42592 100644 --- a/Source/WebCore/dom/PositionIterator.h +++ b/Source/WebCore/dom/PositionIterator.h @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PositionIterator_h -#define PositionIterator_h +#pragma once #include "Node.h" #include "Position.h" @@ -36,16 +35,9 @@ namespace WebCore { // Conversion to/from Position is O(n) in the offset. class PositionIterator { public: - PositionIterator() - : m_anchorNode(0) - , m_nodeAfterPositionInAnchor(0) - , m_offsetInAnchor(0) - { - } - PositionIterator(const Position& pos) : m_anchorNode(pos.anchorNode()) - , m_nodeAfterPositionInAnchor(m_anchorNode->childNode(pos.deprecatedEditingOffset())) + , m_nodeAfterPositionInAnchor(m_anchorNode->traverseToChildAt(pos.deprecatedEditingOffset())) , m_offsetInAnchor(m_nodeAfterPositionInAnchor ? 0 : pos.deprecatedEditingOffset()) { } @@ -64,11 +56,9 @@ public: bool isCandidate() const; private: - Node* m_anchorNode; - Node* m_nodeAfterPositionInAnchor; // If this is non-null, m_nodeAfterPositionInAnchor->parentNode() == m_anchorNode; - int m_offsetInAnchor; + Node* m_anchorNode { nullptr }; + Node* m_nodeAfterPositionInAnchor { nullptr }; // If this is non-null, m_nodeAfterPositionInAnchor->parentNode() == m_anchorNode; + int m_offsetInAnchor { 0 }; }; } // namespace WebCore - -#endif // PositionIterator_h diff --git a/Source/WebCore/dom/ProcessingInstruction.cpp b/Source/WebCore/dom/ProcessingInstruction.cpp index 249bba8a4..da405428c 100644 --- a/Source/WebCore/dom/ProcessingInstruction.cpp +++ b/Source/WebCore/dom/ProcessingInstruction.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2000 Peter Kelly (pmk@post.com) - * Copyright (C) 2006, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2006-2017 Apple Inc. All rights reserved. * Copyright (C) 2013 Samsung Electronics. All rights reserved. * * This library is free software; you can redistribute it and/or @@ -28,33 +28,26 @@ #include "CachedResourceRequest.h" #include "CachedXSLStyleSheet.h" #include "Document.h" -#include "ExceptionCode.h" #include "Frame.h" #include "FrameLoader.h" -#include "XSLStyleSheet.h" -#include "XMLDocumentParser.h" // for parseAttributes() #include "MediaList.h" +#include "StyleScope.h" #include "StyleSheetContents.h" +#include "XMLDocumentParser.h" +#include "XSLStyleSheet.h" +#include <wtf/SetForScope.h> namespace WebCore { inline ProcessingInstruction::ProcessingInstruction(Document& document, const String& target, const String& data) : CharacterData(document, data, CreateOther) , m_target(target) - , m_cachedSheet(0) - , m_loading(false) - , m_alternate(false) - , m_createdByParser(false) - , m_isCSS(false) -#if ENABLE(XSLT) - , m_isXSL(false) -#endif { } -PassRefPtr<ProcessingInstruction> ProcessingInstruction::create(Document& document, const String& target, const String& data) +Ref<ProcessingInstruction> ProcessingInstruction::create(Document& document, const String& target, const String& data) { - return adoptRef(new ProcessingInstruction(document, target, data)); + return adoptRef(*new ProcessingInstruction(document, target, data)); } ProcessingInstruction::~ProcessingInstruction() @@ -63,10 +56,10 @@ ProcessingInstruction::~ProcessingInstruction() m_sheet->clearOwnerNode(); if (m_cachedSheet) - m_cachedSheet->removeClient(this); + m_cachedSheet->removeClient(*this); - if (inDocument()) - document().styleSheetCollection().removeStyleSheetCandidateNode(*this); + if (isConnected()) + document().styleScope().removeStyleSheetCandidateNode(*this); } String ProcessingInstruction::nodeName() const @@ -79,15 +72,19 @@ Node::NodeType ProcessingInstruction::nodeType() const return PROCESSING_INSTRUCTION_NODE; } -PassRefPtr<Node> ProcessingInstruction::cloneNode(bool /*deep*/) +Ref<Node> ProcessingInstruction::cloneNodeInternal(Document& targetDocument, CloningOperation) { // FIXME: Is it a problem that this does not copy m_localHref? // What about other data members? - return create(document(), m_target, data()); + return create(targetDocument, m_target, data()); } void ProcessingInstruction::checkStyleSheet() { + // Prevent recursive loading of stylesheet. + if (m_isHandlingBeforeLoad) + return; + if (m_target == "xml-stylesheet" && document().frame() && parentNode() == &document()) { // see http://www.w3.org/TR/xml-stylesheet/ // ### support stylesheet included in a fragment of this (or another) document @@ -133,37 +130,52 @@ void ProcessingInstruction::checkStyleSheet() #endif } else { if (m_cachedSheet) { - m_cachedSheet->removeClient(this); - m_cachedSheet = 0; + m_cachedSheet->removeClient(*this); + m_cachedSheet = nullptr; } - + String url = document().completeURL(href).string(); + + Ref<Document> originalDocument = document(); + + { + SetForScope<bool> change(m_isHandlingBeforeLoad, true); if (!dispatchBeforeLoadEvent(url)) return; - + } + + bool didEventListenerDisconnectThisElement = !isConnected() || &document() != originalDocument.ptr(); + if (didEventListenerDisconnectThisElement) + return; + m_loading = true; - document().styleSheetCollection().addPendingSheet(); - - CachedResourceRequest request(ResourceRequest(document().completeURL(href))); + document().styleScope().addPendingSheet(); + + ASSERT_WITH_SECURITY_IMPLICATION(!m_cachedSheet); + #if ENABLE(XSLT) - if (m_isXSL) - m_cachedSheet = document().cachedResourceLoader()->requestXSLStyleSheet(request); - else + if (m_isXSL) { + auto options = CachedResourceLoader::defaultCachedResourceOptions(); + options.mode = FetchOptions::Mode::SameOrigin; + m_cachedSheet = document().cachedResourceLoader().requestXSLStyleSheet({ResourceRequest(document().completeURL(href)), options}); + } else #endif { String charset = attrs.get("charset"); - if (charset.isEmpty()) - charset = document().charset(); - request.setCharset(charset); + CachedResourceRequest request(document().completeURL(href), CachedResourceLoader::defaultCachedResourceOptions(), std::nullopt, charset.isEmpty() ? document().charset() : WTFMove(charset)); - m_cachedSheet = document().cachedResourceLoader()->requestCSSStyleSheet(request); + m_cachedSheet = document().cachedResourceLoader().requestCSSStyleSheet(WTFMove(request)); } if (m_cachedSheet) - m_cachedSheet->addClient(this); + m_cachedSheet->addClient(*this); else { // The request may have been denied if (for example) the stylesheet is local and the document is remote. m_loading = false; - document().styleSheetCollection().removePendingSheet(); + document().styleScope().removePendingSheet(); +#if ENABLE(XSLT) + if (m_isXSL) + document().styleScope().flushPendingUpdate(); +#endif } } } @@ -181,7 +193,11 @@ bool ProcessingInstruction::isLoading() const bool ProcessingInstruction::sheetLoaded() { if (!isLoading()) { - document().styleSheetCollection().removePendingSheet(); + document().styleScope().removePendingSheet(); +#if ENABLE(XSLT) + if (m_isXSL) + document().styleScope().flushPendingUpdate(); +#endif return true; } return false; @@ -189,7 +205,7 @@ bool ProcessingInstruction::sheetLoaded() void ProcessingInstruction::setCSSStyleSheet(const String& href, const URL& baseURL, const String& charset, const CachedCSSStyleSheet* sheet) { - if (!inDocument()) { + if (!isConnected()) { ASSERT(!m_sheet); return; } @@ -197,17 +213,18 @@ void ProcessingInstruction::setCSSStyleSheet(const String& href, const URL& base ASSERT(m_isCSS); CSSParserContext parserContext(document(), baseURL, charset); - auto cssSheet = CSSStyleSheet::create(StyleSheetContents::create(href, parserContext), this); + auto cssSheet = CSSStyleSheet::create(StyleSheetContents::create(href, parserContext), *this); cssSheet.get().setDisabled(m_alternate); cssSheet.get().setTitle(m_title); cssSheet.get().setMediaQueries(MediaQuerySet::create(m_media)); - m_sheet = std::move(cssSheet); + m_sheet = WTFMove(cssSheet); // We don't need the cross-origin security check here because we are // getting the sheet text in "strict" mode. This enforces a valid CSS MIME // type. - parseStyleSheet(sheet->sheetText(true)); + Ref<Document> protect(document()); + parseStyleSheet(sheet->sheetText()); } #if ENABLE(XSLT) @@ -223,35 +240,26 @@ void ProcessingInstruction::setXSLStyleSheet(const String& href, const URL& base void ProcessingInstruction::parseStyleSheet(const String& sheet) { if (m_isCSS) - static_cast<CSSStyleSheet*>(m_sheet.get())->contents().parseString(sheet); + downcast<CSSStyleSheet>(*m_sheet).contents().parseString(sheet); #if ENABLE(XSLT) else if (m_isXSL) - static_cast<XSLStyleSheet*>(m_sheet.get())->parseString(sheet); + downcast<XSLStyleSheet>(*m_sheet).parseString(sheet); #endif if (m_cachedSheet) - m_cachedSheet->removeClient(this); - m_cachedSheet = 0; + m_cachedSheet->removeClient(*this); + m_cachedSheet = nullptr; m_loading = false; if (m_isCSS) - static_cast<CSSStyleSheet*>(m_sheet.get())->contents().checkLoaded(); + downcast<CSSStyleSheet>(*m_sheet).contents().checkLoaded(); #if ENABLE(XSLT) else if (m_isXSL) - static_cast<XSLStyleSheet*>(m_sheet.get())->checkLoaded(); + downcast<XSLStyleSheet>(*m_sheet).checkLoaded(); #endif } -void ProcessingInstruction::setCSSStyleSheet(PassRefPtr<CSSStyleSheet> sheet) -{ - ASSERT(!m_cachedSheet); - ASSERT(!m_loading); - m_sheet = sheet; - sheet->setTitle(m_title); - sheet->setDisabled(m_alternate); -} - void ProcessingInstruction::addSubresourceAttributeURLs(ListHashSet<URL>& urls) const { if (!sheet()) @@ -263,9 +271,9 @@ void ProcessingInstruction::addSubresourceAttributeURLs(ListHashSet<URL>& urls) Node::InsertionNotificationRequest ProcessingInstruction::insertedInto(ContainerNode& insertionPoint) { CharacterData::insertedInto(insertionPoint); - if (!insertionPoint.inDocument()) + if (!insertionPoint.isConnected()) return InsertionDone; - document().styleSheetCollection().addStyleSheetCandidateNode(*this, m_createdByParser); + document().styleScope().addStyleSheetCandidateNode(*this, m_createdByParser); checkStyleSheet(); return InsertionDone; } @@ -273,20 +281,23 @@ Node::InsertionNotificationRequest ProcessingInstruction::insertedInto(Container void ProcessingInstruction::removedFrom(ContainerNode& insertionPoint) { CharacterData::removedFrom(insertionPoint); - if (!insertionPoint.inDocument()) + if (!insertionPoint.isConnected()) return; - document().styleSheetCollection().removeStyleSheetCandidateNode(*this); + document().styleScope().removeStyleSheetCandidateNode(*this); if (m_sheet) { ASSERT(m_sheet->ownerNode() == this); m_sheet->clearOwnerNode(); - m_sheet = 0; + m_sheet = nullptr; + } + + if (m_loading) { + m_loading = false; + document().styleScope().removePendingSheet(); } - // If we're in document teardown, then we don't need to do any notification of our sheet's removal. - if (document().hasLivingRenderTree()) - document().styleResolverChanged(DeferRecalcStyle); + document().styleScope().didChangeActiveStyleSheetCandidates(); } void ProcessingInstruction::finishParsingChildren() diff --git a/Source/WebCore/dom/ProcessingInstruction.h b/Source/WebCore/dom/ProcessingInstruction.h index 7d4558530..d15443328 100644 --- a/Source/WebCore/dom/ProcessingInstruction.h +++ b/Source/WebCore/dom/ProcessingInstruction.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2000 Peter Kelly (pmk@post.com) - * Copyright (C) 2006 Apple Inc. All rights reserved. + * Copyright (C) 2006-2017 Apple Inc. All rights reserved. * Copyright (C) 2013 Samsung Electronics. All rights reserved. * * This library is free software; you can redistribute it and/or @@ -20,8 +20,7 @@ * */ -#ifndef ProcessingInstruction_h -#define ProcessingInstruction_h +#pragma once #include "CachedResourceHandle.h" #include "CachedStyleSheetClient.h" @@ -34,18 +33,17 @@ class CSSStyleSheet; class ProcessingInstruction final : public CharacterData, private CachedStyleSheetClient { public: - static PassRefPtr<ProcessingInstruction> create(Document&, const String& target, const String& data); + static Ref<ProcessingInstruction> create(Document&, const String& target, const String& data); virtual ~ProcessingInstruction(); const String& target() const { return m_target; } void setCreatedByParser(bool createdByParser) { m_createdByParser = createdByParser; } - virtual void finishParsingChildren() override; + void finishParsingChildren() override; const String& localHref() const { return m_localHref; } StyleSheet* sheet() const { return m_sheet.get(); } - void setCSSStyleSheet(PassRefPtr<CSSStyleSheet>); bool isCSS() const { return m_isCSS; } #if ENABLE(XSLT) @@ -56,23 +54,23 @@ private: friend class CharacterData; ProcessingInstruction(Document&, const String& target, const String& data); - virtual String nodeName() const override; - virtual NodeType nodeType() const override; - virtual PassRefPtr<Node> cloneNode(bool deep) override; + String nodeName() const override; + NodeType nodeType() const override; + Ref<Node> cloneNodeInternal(Document&, CloningOperation) override; - virtual InsertionNotificationRequest insertedInto(ContainerNode&) override; - virtual void removedFrom(ContainerNode&) override; + InsertionNotificationRequest insertedInto(ContainerNode&) override; + void removedFrom(ContainerNode&) override; void checkStyleSheet(); - virtual void setCSSStyleSheet(const String& href, const URL& baseURL, const String& charset, const CachedCSSStyleSheet*) override; + void setCSSStyleSheet(const String& href, const URL& baseURL, const String& charset, const CachedCSSStyleSheet*) override; #if ENABLE(XSLT) - virtual void setXSLStyleSheet(const String& href, const URL& baseURL, const String& sheet) override; + void setXSLStyleSheet(const String& href, const URL& baseURL, const String& sheet) override; #endif bool isLoading() const; - virtual bool sheetLoaded() override; + bool sheetLoaded() override; - virtual void addSubresourceAttributeURLs(ListHashSet<URL>&) const override; + void addSubresourceAttributeURLs(ListHashSet<URL>&) const override; void parseStyleSheet(const String& sheet); @@ -80,24 +78,20 @@ private: String m_localHref; String m_title; String m_media; - CachedResourceHandle<CachedResource> m_cachedSheet; + CachedResourceHandle<CachedResource> m_cachedSheet { nullptr }; RefPtr<StyleSheet> m_sheet; - bool m_loading; - bool m_alternate; - bool m_createdByParser; - bool m_isCSS; + bool m_loading { false }; + bool m_alternate { false }; + bool m_createdByParser { false }; + bool m_isCSS { false }; #if ENABLE(XSLT) - bool m_isXSL; + bool m_isXSL { false }; #endif + bool m_isHandlingBeforeLoad { false }; }; -inline bool isProcessingInstruction(const Node& node) -{ - return node.nodeType() == Node::PROCESSING_INSTRUCTION_NODE; -} +} // namespace WebCore -NODE_TYPE_CASTS(ProcessingInstruction) - -} //namespace - -#endif +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ProcessingInstruction) + static bool isType(const WebCore::Node& node) { return node.nodeType() == WebCore::Node::PROCESSING_INSTRUCTION_NODE; } +SPECIALIZE_TYPE_TRAITS_END() diff --git a/Source/WebCore/dom/ProcessingInstruction.idl b/Source/WebCore/dom/ProcessingInstruction.idl index ba712f954..c519fe412 100644 --- a/Source/WebCore/dom/ProcessingInstruction.idl +++ b/Source/WebCore/dom/ProcessingInstruction.idl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006 Apple Inc. * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> * Copyright (C) 2013 Samsung Electronics * @@ -19,11 +19,13 @@ * Boston, MA 02110-1301, USA. */ -interface ProcessingInstruction : CharacterData { +[ + JSGenerateToJSObject +] interface ProcessingInstruction : CharacterData { // DOM Level 1 - [TreatReturnedNullStringAs=Null] readonly attribute DOMString target; + readonly attribute DOMString? target; // interface LinkStyle from DOM Level 2 Style Sheets readonly attribute StyleSheet sheet; diff --git a/Source/WebCore/dom/ProgressEvent.cpp b/Source/WebCore/dom/ProgressEvent.cpp index c8f830b0e..7978212f3 100644 --- a/Source/WebCore/dom/ProgressEvent.cpp +++ b/Source/WebCore/dom/ProgressEvent.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,26 +26,10 @@ #include "config.h" #include "ProgressEvent.h" -#include "EventNames.h" - namespace WebCore { -ProgressEventInit::ProgressEventInit() - : lengthComputable(false) - , loaded(0) - , total(0) -{ -} - -ProgressEvent::ProgressEvent() - : m_lengthComputable(false) - , m_loaded(0) - , m_total(0) -{ -} - -ProgressEvent::ProgressEvent(const AtomicString& type, const ProgressEventInit& initializer) - : Event(type, initializer) +ProgressEvent::ProgressEvent(const AtomicString& type, const Init& initializer, IsTrusted isTrusted) + : Event(type, initializer, isTrusted) , m_lengthComputable(initializer.lengthComputable) , m_loaded(initializer.loaded) , m_total(initializer.total) diff --git a/Source/WebCore/dom/ProgressEvent.h b/Source/WebCore/dom/ProgressEvent.h index 8d23506cb..ffabcb8b1 100644 --- a/Source/WebCore/dom/ProgressEvent.h +++ b/Source/WebCore/dom/ProgressEvent.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -23,52 +23,44 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ProgressEvent_h -#define ProgressEvent_h +#pragma once #include "Event.h" namespace WebCore { -struct ProgressEventInit : public EventInit { - ProgressEventInit(); - - bool lengthComputable; - unsigned long long loaded; - unsigned long long total; -}; - class ProgressEvent : public Event { public: - static PassRefPtr<ProgressEvent> create() + static Ref<ProgressEvent> create(const AtomicString& type, bool lengthComputable, unsigned long long loaded, unsigned long long total) { - return adoptRef(new ProgressEvent); + return adoptRef(*new ProgressEvent(type, lengthComputable, loaded, total)); } - static PassRefPtr<ProgressEvent> create(const AtomicString& type, bool lengthComputable, unsigned long long loaded, unsigned long long total) - { - return adoptRef(new ProgressEvent(type, lengthComputable, loaded, total)); - } - static PassRefPtr<ProgressEvent> create(const AtomicString& type, const ProgressEventInit& initializer) + + struct Init : EventInit { + bool lengthComputable { false }; + unsigned long long loaded { 0 }; + unsigned long long total { 0 }; + }; + + static Ref<ProgressEvent> create(const AtomicString& type, const Init& initializer, IsTrusted isTrusted = IsTrusted::No) { - return adoptRef(new ProgressEvent(type, initializer)); + return adoptRef(*new ProgressEvent(type, initializer, isTrusted)); } bool lengthComputable() const { return m_lengthComputable; } unsigned long long loaded() const { return m_loaded; } unsigned long long total() const { return m_total; } - virtual EventInterface eventInterface() const override; + EventInterface eventInterface() const override; protected: - ProgressEvent(); ProgressEvent(const AtomicString& type, bool lengthComputable, unsigned long long loaded, unsigned long long total); - ProgressEvent(const AtomicString&, const ProgressEventInit&); + ProgressEvent(const AtomicString&, const Init&, IsTrusted); private: bool m_lengthComputable; unsigned long long m_loaded; unsigned long long m_total; }; -} -#endif // ProgressEvent_h +} // namespace WebCore diff --git a/Source/WebCore/dom/ProgressEvent.idl b/Source/WebCore/dom/ProgressEvent.idl index ae25a9564..dfb904824 100644 --- a/Source/WebCore/dom/ProgressEvent.idl +++ b/Source/WebCore/dom/ProgressEvent.idl @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,11 +24,16 @@ */ [ - ConstructorTemplate=Event, - JSNoStaticTables + Constructor(DOMString type, optional ProgressEventInit eventInitDict), + Exposed=(Window,Worker), ] interface ProgressEvent : Event { - [InitializedByEventConstructor] readonly attribute boolean lengthComputable; - [InitializedByEventConstructor] readonly attribute unsigned long long loaded; - [InitializedByEventConstructor] readonly attribute unsigned long long total; + readonly attribute boolean lengthComputable; + readonly attribute unsigned long long loaded; + readonly attribute unsigned long long total; }; +dictionary ProgressEventInit : EventInit { + boolean lengthComputable = false; + unsigned long long loaded = 0; + unsigned long long total = 0; +}; diff --git a/Source/WebCore/dom/PseudoElement.cpp b/Source/WebCore/dom/PseudoElement.cpp index 29259e2ec..4dadfbaa4 100644 --- a/Source/WebCore/dom/PseudoElement.cpp +++ b/Source/WebCore/dom/PseudoElement.cpp @@ -32,19 +32,20 @@ #include "RenderElement.h" #include "RenderImage.h" #include "RenderQuote.h" +#include "StyleResolver.h" namespace WebCore { const QualifiedName& pseudoElementTagName() { - DEFINE_STATIC_LOCAL(QualifiedName, name, (nullAtom, "<pseudo>", nullAtom)); + static NeverDestroyed<QualifiedName> name(nullAtom, "<pseudo>", nullAtom); return name; } String PseudoElement::pseudoElementNameForEvents(PseudoId pseudoId) { - DEFINE_STATIC_LOCAL(const String, after, (ASCIILiteral("::after"))); - DEFINE_STATIC_LOCAL(const String, before, (ASCIILiteral("::before"))); + static NeverDestroyed<const String> after(ASCIILiteral("::after")); + static NeverDestroyed<const String> before(ASCIILiteral("::before")); switch (pseudoId) { case AFTER: return after; @@ -67,14 +68,21 @@ PseudoElement::PseudoElement(Element& host, PseudoId pseudoId) PseudoElement::~PseudoElement() { ASSERT(!m_hostElement); -#if USE(ACCELERATED_COMPOSITING) - InspectorInstrumentation::pseudoElementDestroyed(document().page(), this); -#endif } -PassRefPtr<RenderStyle> PseudoElement::customStyleForRenderer() +void PseudoElement::clearHostElement() { - return m_hostElement->renderer()->getCachedPseudoStyle(m_pseudoId); + InspectorInstrumentation::pseudoElementDestroyed(document().page(), *this); + + m_hostElement = nullptr; +} + +std::optional<ElementStyle> PseudoElement::resolveCustomStyle(const RenderStyle& parentStyle, const RenderStyle*) +{ + auto* style = m_hostElement->renderer()->getCachedPseudoStyle(m_pseudoId, &parentStyle); + if (!style) + return std::nullopt; + return ElementStyle(RenderStyle::clonePtr(*style)); } void PseudoElement::didAttachRenderers() @@ -88,12 +96,8 @@ void PseudoElement::didAttachRenderers() for (const ContentData* content = style.contentData(); content; content = content->next()) { auto child = content->createContentRenderer(document(), style); - if (renderer->isChildAllowed(*child, style)) { - auto* childPtr = child.get(); + if (renderer->isChildAllowed(*child, style)) renderer->addChild(child.leakPtr()); - if (childPtr->isQuote()) - toRenderQuote(childPtr)->attachQuote(); - } } } @@ -109,13 +113,13 @@ void PseudoElement::didRecalcStyle(Style::Change) // The renderers inside pseudo elements are anonymous so they don't get notified of recalcStyle and must have // the style propagated downward manually similar to RenderObject::propagateStyleToAnonymousChildren. - RenderObject* renderer = this->renderer(); - for (RenderObject* child = renderer->nextInPreOrder(renderer); child; child = child->nextInPreOrder(renderer)) { + RenderElement& renderer = *this->renderer(); + for (RenderObject* child = renderer.nextInPreOrder(&renderer); child; child = child->nextInPreOrder(&renderer)) { // We only manage the style for the generated content which must be images or text. - if (!child->isRenderImage() && !child->isQuote()) + if (!is<RenderImage>(*child) && !is<RenderQuote>(*child)) continue; - PassRef<RenderStyle> createdStyle = RenderStyle::createStyleInheritingFromPseudoStyle(renderer->style()); - toRenderElement(*child).setStyle(std::move(createdStyle)); + auto createdStyle = RenderStyle::createStyleInheritingFromPseudoStyle(renderer.style()); + downcast<RenderElement>(*child).setStyle(WTFMove(createdStyle)); } } diff --git a/Source/WebCore/dom/PseudoElement.h b/Source/WebCore/dom/PseudoElement.h index 10bb186a6..367ada576 100644 --- a/Source/WebCore/dom/PseudoElement.h +++ b/Source/WebCore/dom/PseudoElement.h @@ -24,47 +24,45 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PseudoElement_h -#define PseudoElement_h +#pragma once #include "Element.h" #include "Event.h" -#include "RenderStyle.h" #include <wtf/Forward.h> namespace WebCore { class PseudoElement final : public Element { public: - static PassRefPtr<PseudoElement> create(Element& host, PseudoId pseudoId) + static Ref<PseudoElement> create(Element& host, PseudoId pseudoId) { - return adoptRef(new PseudoElement(host, pseudoId)); + return adoptRef(*new PseudoElement(host, pseudoId)); } virtual ~PseudoElement(); Element* hostElement() const { return m_hostElement; } - void clearHostElement() { m_hostElement = nullptr; } + void clearHostElement(); - virtual PassRefPtr<RenderStyle> customStyleForRenderer() override; - virtual void didAttachRenderers() override; - virtual bool rendererIsNeeded(const RenderStyle&) override; + std::optional<ElementStyle> resolveCustomStyle(const RenderStyle& parentStyle, const RenderStyle* shadowHostStyle) override; + void didAttachRenderers() override; + void didRecalcStyle(Style::Change) override; + bool rendererIsNeeded(const RenderStyle&) override; // As per http://dev.w3.org/csswg/css3-regions/#flow-into, pseudo-elements such as ::first-line, ::first-letter, ::before or ::after // cannot be directly collected into a named flow. #if ENABLE(CSS_REGIONS) - virtual bool shouldMoveToFlowThread(const RenderStyle&) const override { return false; } + bool shouldMoveToFlowThread(const RenderStyle&) const override { return false; } #endif - virtual bool canStartSelection() const override { return false; } - virtual bool canContainRangeEndPoint() const override { return false; } + bool canStartSelection() const override { return false; } + bool canContainRangeEndPoint() const override { return false; } static String pseudoElementNameForEvents(PseudoId); private: PseudoElement(Element&, PseudoId); - virtual void didRecalcStyle(Style::Change) override; - virtual PseudoId customPseudoId() const override { return m_pseudoId; } + PseudoId customPseudoId() const override { return m_pseudoId; } Element* m_hostElement; PseudoId m_pseudoId; @@ -72,15 +70,8 @@ private: const QualifiedName& pseudoElementTagName(); -inline bool pseudoElementRendererIsNeeded(const RenderStyle* style) -{ - return style && style->display() != NONE && (style->contentData() || style->hasFlowFrom()); -} +} // namespace WebCore -void isPseudoElement(const PseudoElement&); // Catch unnecessary runtime check of type known at compile time. -inline bool isPseudoElement(const Node& node) { return node.isPseudoElement(); } -NODE_TYPE_CASTS(PseudoElement) - -} // namespace - -#endif +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::PseudoElement) + static bool isType(const WebCore::Node& node) { return node.isPseudoElement(); } +SPECIALIZE_TYPE_TRAITS_END() diff --git a/Source/WebCore/dom/QualifiedName.cpp b/Source/WebCore/dom/QualifiedName.cpp index 36aa99457..638bb916e 100644 --- a/Source/WebCore/dom/QualifiedName.cpp +++ b/Source/WebCore/dom/QualifiedName.cpp @@ -27,31 +27,26 @@ #include "QualifiedName.h" #include "HTMLNames.h" +#include "SVGNames.h" #include "XLinkNames.h" #include "XMLNSNames.h" #include "XMLNames.h" #include <wtf/Assertions.h> #include <wtf/HashSet.h> +#include <wtf/NeverDestroyed.h> #include <wtf/StaticConstructors.h> -#include <wtf/text/StringBuilder.h> #if ENABLE(MATHML) #include "MathMLNames.h" #endif -#if ENABLE(SVG) -#include "SVGNames.h" -#endif - namespace WebCore { static const int staticQualifiedNamesCount = HTMLNames::HTMLTagsCount + HTMLNames::HTMLAttrsCount #if ENABLE(MATHML) + MathMLNames::MathMLTagsCount + MathMLNames::MathMLAttrsCount #endif -#if ENABLE(SVG) + SVGNames::SVGTagsCount + SVGNames::SVGAttrsCount -#endif + XLinkNames::XLinkAttrsCount + XMLNSNames::XMLNSAttrsCount + XMLNames::XMLAttrsCount; @@ -73,38 +68,21 @@ struct QNameComponentsTranslator { } static void translate(QualifiedName::QualifiedNameImpl*& location, const QualifiedNameComponents& components, unsigned) { - location = QualifiedName::QualifiedNameImpl::create(components.m_prefix, components.m_localName, components.m_namespace).leakRef(); + location = &QualifiedName::QualifiedNameImpl::create(components.m_prefix, components.m_localName, components.m_namespace).leakRef(); } }; static inline QNameSet& qualifiedNameCache() { - DEFINE_STATIC_LOCAL(QNameSet, nameCache, ()); + static NeverDestroyed<QNameSet> nameCache; return nameCache; } QualifiedName::QualifiedName(const AtomicString& p, const AtomicString& l, const AtomicString& n) { - QualifiedNameComponents components = { p.impl(), l.impl(), n.isEmpty() ? nullAtom.impl() : n.impl() }; + QualifiedNameComponents components = { p.impl(), l.impl(), n.isEmpty() ? nullptr : n.impl() }; QNameSet::AddResult addResult = qualifiedNameCache().add<QNameComponentsTranslator>(components); - m_impl = *addResult.iterator; - if (!addResult.isNewEntry) - m_impl->ref(); -} - -QualifiedName::~QualifiedName() -{ - deref(); -} - -void QualifiedName::deref() -{ -#ifdef QNAME_DEFAULT_CONSTRUCTOR - if (!m_impl) - return; -#endif - ASSERT(!isHashTableDeletedValue()); - m_impl->deref(); + m_impl = addResult.isNewEntry ? adoptRef(*addResult.iterator) : *addResult.iterator; } QualifiedName::QualifiedNameImpl::~QualifiedNameImpl() @@ -112,14 +90,6 @@ QualifiedName::QualifiedNameImpl::~QualifiedNameImpl() qualifiedNameCache().remove(this); } -String QualifiedName::toString() const -{ - if (!hasPrefix()) - return localName(); - - return prefix().string() + ':' + localName().string(); -} - // Global init routines DEFINE_GLOBAL(QualifiedName, anyName, nullAtom, starAtom, starAtom) @@ -137,14 +107,14 @@ void QualifiedName::init() const QualifiedName& nullQName() { - DEFINE_STATIC_LOCAL(QualifiedName, nullName, (nullAtom, nullAtom, nullAtom)); + static NeverDestroyed<QualifiedName> nullName(nullAtom, nullAtom, nullAtom); return nullName; } const AtomicString& QualifiedName::localNameUpper() const { if (!m_impl->m_localNameUpper) - m_impl->m_localNameUpper = m_impl->m_localName.upper(); + m_impl->m_localNameUpper = m_impl->m_localName.convertToASCIIUppercase(); return m_impl->m_localNameUpper; } diff --git a/Source/WebCore/dom/QualifiedName.h b/Source/WebCore/dom/QualifiedName.h index c64ac2dd1..ce03e9ad6 100644 --- a/Source/WebCore/dom/QualifiedName.h +++ b/Source/WebCore/dom/QualifiedName.h @@ -18,8 +18,7 @@ * */ -#ifndef QualifiedName_h -#define QualifiedName_h +#pragma once #include <wtf/Forward.h> #include <wtf/HashTraits.h> @@ -39,12 +38,12 @@ class QualifiedName { public: class QualifiedNameImpl : public RefCounted<QualifiedNameImpl> { public: - static PassRefPtr<QualifiedNameImpl> create(const AtomicString& prefix, const AtomicString& localName, const AtomicString& namespaceURI) + static Ref<QualifiedNameImpl> create(const AtomicString& prefix, const AtomicString& localName, const AtomicString& namespaceURI) { - return adoptRef(new QualifiedNameImpl(prefix, localName, namespaceURI)); + return adoptRef(*new QualifiedNameImpl(prefix, localName, namespaceURI)); } - ~QualifiedNameImpl(); + WEBCORE_EXPORT ~QualifiedNameImpl(); unsigned computeHash() const; @@ -54,10 +53,10 @@ public: const AtomicString m_namespace; mutable AtomicString m_localNameUpper; -#if ENABLE(CSS_SELECTOR_JIT) +#if ENABLE(JIT) static ptrdiff_t localNameMemoryOffset() { return OBJECT_OFFSETOF(QualifiedNameImpl, m_localName); } static ptrdiff_t namespaceMemoryOffset() { return OBJECT_OFFSETOF(QualifiedNameImpl, m_namespace); } -#endif // ENABLE(CSS_SELECTOR_JIT) +#endif // ENABLE(JIT) private: QualifiedNameImpl(const AtomicString& prefix, const AtomicString& localName, const AtomicString& namespaceURI) @@ -70,24 +69,18 @@ public: } }; - QualifiedName(const AtomicString& prefix, const AtomicString& localName, const AtomicString& namespaceURI); - explicit QualifiedName(WTF::HashTableDeletedValueType) : m_impl(hashTableDeletedValue()) { } - bool isHashTableDeletedValue() const { return m_impl == hashTableDeletedValue(); } - ~QualifiedName(); + WEBCORE_EXPORT QualifiedName(const AtomicString& prefix, const AtomicString& localName, const AtomicString& namespaceURI); + explicit QualifiedName(WTF::HashTableDeletedValueType) : m_impl(WTF::HashTableDeletedValue) { } + bool isHashTableDeletedValue() const { return m_impl.isHashTableDeletedValue(); } #ifdef QNAME_DEFAULT_CONSTRUCTOR - QualifiedName() : m_impl(0) { } + QualifiedName() { } #endif - QualifiedName(const QualifiedName& other) : m_impl(other.m_impl) { ref(); } - const QualifiedName& operator=(const QualifiedName& other) { other.ref(); deref(); m_impl = other.m_impl; return *this; } - bool operator==(const QualifiedName& other) const { return m_impl == other.m_impl; } bool operator!=(const QualifiedName& other) const { return !(*this == other); } bool matches(const QualifiedName& other) const { return m_impl == other.m_impl || (localName() == other.localName() && namespaceURI() == other.namespaceURI()); } - bool matchesIgnoringCaseForLocalName(const QualifiedName& other, bool shouldIgnoreCase) const { return m_impl == other.m_impl || (equalPossiblyIgnoringCase(localName(), other.localName(), shouldIgnoreCase) && namespaceURI() == other.namespaceURI()); } - bool hasPrefix() const { return m_impl->m_prefix != nullAtom; } void setPrefix(const AtomicString& prefix) { *this = QualifiedName(prefix, localName(), namespaceURI()); } @@ -100,21 +93,18 @@ public: String toString() const; - QualifiedNameImpl* impl() const { return m_impl; } -#if ENABLE(CSS_SELECTOR_JIT) + QualifiedNameImpl* impl() const { return m_impl.get(); } +#if ENABLE(JIT) static ptrdiff_t implMemoryOffset() { return OBJECT_OFFSETOF(QualifiedName, m_impl); } -#endif // ENABLE(CSS_SELECTOR_JIT) +#endif // ENABLE(JIT) // Init routine for globals static void init(); private: - void ref() const { m_impl->ref(); } - void deref(); - static QualifiedNameImpl* hashTableDeletedValue() { return RefPtr<QualifiedNameImpl>::hashTableDeletedValue(); } - QualifiedNameImpl* m_impl; + RefPtr<QualifiedNameImpl> m_impl; }; #ifndef WEBCORE_QUALIFIEDNAME_HIDE_GLOBALS @@ -153,8 +143,16 @@ struct QualifiedNameHash { void createQualifiedName(void* targetAddress, StringImpl* name); void createQualifiedName(void* targetAddress, StringImpl* name, const AtomicString& nameNamespace); +inline String QualifiedName::toString() const +{ + if (!hasPrefix()) + return localName(); + + return prefix().string() + ':' + localName().string(); } +} // namespace WebCore + namespace WTF { template<typename T> struct DefaultHash; @@ -167,6 +165,5 @@ namespace WTF { static const bool emptyValueIsZero = false; static WebCore::QualifiedName emptyValue() { return WebCore::nullQName(); } }; -} -#endif +} // namespace WTF diff --git a/Source/WebCore/dom/CheckedRadioButtons.cpp b/Source/WebCore/dom/RadioButtonGroups.cpp index 5e223b918..4d337279c 100644 --- a/Source/WebCore/dom/CheckedRadioButtons.cpp +++ b/Source/WebCore/dom/RadioButtonGroups.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009, 2016 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -19,9 +19,10 @@ */ #include "config.h" -#include "CheckedRadioButtons.h" +#include "RadioButtonGroups.h" #include "HTMLInputElement.h" +#include "Range.h" #include <wtf/HashSet.h> namespace WebCore { @@ -29,7 +30,7 @@ namespace WebCore { class RadioButtonGroup { WTF_MAKE_FAST_ALLOCATED; public: - static PassOwnPtr<RadioButtonGroup> create(); + RadioButtonGroup(); bool isEmpty() const { return m_members.isEmpty(); } bool isRequired() const { return m_requiredCount; } HTMLInputElement* checkedButton() const { return m_checkedButton; } @@ -38,11 +39,13 @@ public: void requiredAttributeChanged(HTMLInputElement*); void remove(HTMLInputElement*); bool contains(HTMLInputElement*) const; + Vector<HTMLInputElement*> members() const; private: - RadioButtonGroup(); - void setNeedsValidityCheckForAllButtons(); + void setNeedsStyleRecalcForAllButtons(); + void updateValidityForAllButtons(); bool isValid() const; + void changeCheckedButton(HTMLInputElement*); void setCheckedButton(HTMLInputElement*); HashSet<HTMLInputElement*> m_members; @@ -51,19 +54,22 @@ private: }; RadioButtonGroup::RadioButtonGroup() - : m_checkedButton(0) + : m_checkedButton(nullptr) , m_requiredCount(0) { } -PassOwnPtr<RadioButtonGroup> RadioButtonGroup::create() +inline bool RadioButtonGroup::isValid() const { - return adoptPtr(new RadioButtonGroup); + return !isRequired() || m_checkedButton; } -inline bool RadioButtonGroup::isValid() const +Vector<HTMLInputElement*> RadioButtonGroup::members() const { - return !isRequired() || m_checkedButton; + Vector<HTMLInputElement*> members; + copyToVector(m_members, members); + std::sort(members.begin(), members.end(), documentOrderComparator); + return members; } void RadioButtonGroup::setCheckedButton(HTMLInputElement* button) @@ -71,6 +77,12 @@ void RadioButtonGroup::setCheckedButton(HTMLInputElement* button) HTMLInputElement* oldCheckedButton = m_checkedButton; if (oldCheckedButton == button) return; + + bool hadCheckedButton = m_checkedButton; + bool willHaveCheckedButton = button; + if (hadCheckedButton != willHaveCheckedButton) + setNeedsStyleRecalcForAllButtons(); + m_checkedButton = button; if (oldCheckedButton) oldCheckedButton->setChecked(false); @@ -89,11 +101,11 @@ void RadioButtonGroup::add(HTMLInputElement* button) bool groupIsValid = isValid(); if (groupWasValid != groupIsValid) - setNeedsValidityCheckForAllButtons(); + updateValidityForAllButtons(); else if (!groupIsValid) { // A radio button not in a group is always valid. We need to make it // invalid only if the group is invalid. - button->setNeedsValidityCheck(); + button->updateValidity(); } } @@ -106,10 +118,10 @@ void RadioButtonGroup::updateCheckedState(HTMLInputElement* button) setCheckedButton(button); else { if (m_checkedButton == button) - m_checkedButton = 0; + setCheckedButton(nullptr); } if (wasValid != isValid()) - setNeedsValidityCheckForAllButtons(); + updateValidityForAllButtons(); } void RadioButtonGroup::requiredAttributeChanged(HTMLInputElement* button) @@ -124,7 +136,7 @@ void RadioButtonGroup::requiredAttributeChanged(HTMLInputElement* button) --m_requiredCount; } if (wasValid != isValid()) - setNeedsValidityCheckForAllButtons(); + updateValidityForAllButtons(); } void RadioButtonGroup::remove(HTMLInputElement* button) @@ -133,35 +145,46 @@ void RadioButtonGroup::remove(HTMLInputElement* button) HashSet<HTMLInputElement*>::iterator it = m_members.find(button); if (it == m_members.end()) return; + bool wasValid = isValid(); m_members.remove(it); if (button->isRequired()) { ASSERT(m_requiredCount); --m_requiredCount; } - if (m_checkedButton == button) - m_checkedButton = 0; + if (m_checkedButton) { + button->invalidateStyleForSubtree(); + if (m_checkedButton == button) { + m_checkedButton = nullptr; + setNeedsStyleRecalcForAllButtons(); + } + } if (m_members.isEmpty()) { ASSERT(!m_requiredCount); ASSERT(!m_checkedButton); } else if (wasValid != isValid()) - setNeedsValidityCheckForAllButtons(); + updateValidityForAllButtons(); if (!wasValid) { // A radio button not in a group is always valid. We need to make it // valid only if the group was invalid. - button->setNeedsValidityCheck(); + button->updateValidity(); } } -void RadioButtonGroup::setNeedsValidityCheckForAllButtons() +void RadioButtonGroup::setNeedsStyleRecalcForAllButtons() { - typedef HashSet<HTMLInputElement*>::const_iterator Iterator; - Iterator end = m_members.end(); - for (Iterator it = m_members.begin(); it != end; ++it) { - HTMLInputElement* button = *it; + for (auto& button : m_members) { ASSERT(button->isRadioButton()); - button->setNeedsValidityCheck(); + button->invalidateStyleForSubtree(); + } +} + +void RadioButtonGroup::updateValidityForAllButtons() +{ + for (auto& button : m_members) { + ASSERT(button->isRadioButton()); + button->updateValidity(); } } @@ -175,30 +198,49 @@ bool RadioButtonGroup::contains(HTMLInputElement* button) const // Explicity define empty constructor and destructor in order to prevent the // compiler from generating them as inlines. So we don't need to to define // RadioButtonGroup in the header. -CheckedRadioButtons::CheckedRadioButtons() +RadioButtonGroups::RadioButtonGroups() { } -CheckedRadioButtons::~CheckedRadioButtons() +RadioButtonGroups::~RadioButtonGroups() { } -void CheckedRadioButtons::addButton(HTMLInputElement* element) +void RadioButtonGroups::addButton(HTMLInputElement* element) { ASSERT(element->isRadioButton()); if (element->name().isEmpty()) return; if (!m_nameToGroupMap) - m_nameToGroupMap = adoptPtr(new NameToGroupMap); + m_nameToGroupMap = std::make_unique<NameToGroupMap>(); - OwnPtr<RadioButtonGroup>& group = m_nameToGroupMap->add(element->name().impl(), PassOwnPtr<RadioButtonGroup>()).iterator->value; + auto& group = m_nameToGroupMap->add(element->name().impl(), nullptr).iterator->value; if (!group) - group = RadioButtonGroup::create(); + group = std::make_unique<RadioButtonGroup>(); group->add(element); } -void CheckedRadioButtons::updateCheckedState(HTMLInputElement* element) +Vector<HTMLInputElement*> RadioButtonGroups::groupMembers(const HTMLInputElement& element) const +{ + ASSERT(element.isRadioButton()); + if (!element.isRadioButton()) + return { }; + + auto* name = element.name().impl(); + if (!name) + return { }; + + if (!m_nameToGroupMap) + return { }; + + auto* group = m_nameToGroupMap->get(name); + if (!group) + return { }; + return group->members(); +} + +void RadioButtonGroups::updateCheckedState(HTMLInputElement* element) { ASSERT(element->isRadioButton()); if (element->name().isEmpty()) @@ -211,7 +253,7 @@ void CheckedRadioButtons::updateCheckedState(HTMLInputElement* element) group->updateCheckedState(element); } -void CheckedRadioButtons::requiredAttributeChanged(HTMLInputElement* element) +void RadioButtonGroups::requiredAttributeChanged(HTMLInputElement* element) { ASSERT(element->isRadioButton()); if (element->name().isEmpty()) @@ -224,16 +266,27 @@ void CheckedRadioButtons::requiredAttributeChanged(HTMLInputElement* element) group->requiredAttributeChanged(element); } -HTMLInputElement* CheckedRadioButtons::checkedButtonForGroup(const AtomicString& name) const +HTMLInputElement* RadioButtonGroups::checkedButtonForGroup(const AtomicString& name) const { if (!m_nameToGroupMap) return 0; m_nameToGroupMap->checkConsistency(); RadioButtonGroup* group = m_nameToGroupMap->get(name.impl()); - return group ? group->checkedButton() : 0; + return group ? group->checkedButton() : nullptr; +} + +bool RadioButtonGroups::hasCheckedButton(const HTMLInputElement* element) const +{ + ASSERT(element->isRadioButton()); + const AtomicString& name = element->name(); + if (name.isEmpty() || !m_nameToGroupMap) + return element->checked(); + + const RadioButtonGroup* group = m_nameToGroupMap->get(name.impl()); + return group->checkedButton(); } -bool CheckedRadioButtons::isInRequiredGroup(HTMLInputElement* element) const +bool RadioButtonGroups::isInRequiredGroup(HTMLInputElement* element) const { ASSERT(element->isRadioButton()); if (element->name().isEmpty()) @@ -244,7 +297,7 @@ bool CheckedRadioButtons::isInRequiredGroup(HTMLInputElement* element) const return group && group->isRequired() && group->contains(element); } -void CheckedRadioButtons::removeButton(HTMLInputElement* element) +void RadioButtonGroups::removeButton(HTMLInputElement* element) { ASSERT(element->isRadioButton()); if (element->name().isEmpty()) @@ -263,7 +316,7 @@ void CheckedRadioButtons::removeButton(HTMLInputElement* element) // of m_nameToGroupMap from AtomicStringImpl* to RefPtr<AtomicStringImpl>. m_nameToGroupMap->remove(it); if (m_nameToGroupMap->isEmpty()) - m_nameToGroupMap.clear(); + m_nameToGroupMap = nullptr; } } diff --git a/Source/WebCore/dom/CheckedRadioButtons.h b/Source/WebCore/dom/RadioButtonGroups.h index bb3570da9..1e5a58d57 100644 --- a/Source/WebCore/dom/CheckedRadioButtons.h +++ b/Source/WebCore/dom/RadioButtonGroups.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009, 2016 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -18,36 +18,34 @@ * */ -#ifndef CheckedRadioButtons_h -#define CheckedRadioButtons_h +#pragma once +#include <memory> #include <wtf/Forward.h> #include <wtf/HashMap.h> -#include <wtf/OwnPtr.h> +#include <wtf/Vector.h> namespace WebCore { class HTMLInputElement; class RadioButtonGroup; -// FIXME: Rename the class. The class was a simple map from a name to a checked -// radio button. It manages RadioButtonGroup objects now. -class CheckedRadioButtons { +class RadioButtonGroups { public: - CheckedRadioButtons(); - ~CheckedRadioButtons(); + RadioButtonGroups(); + ~RadioButtonGroups(); void addButton(HTMLInputElement*); void updateCheckedState(HTMLInputElement*); void requiredAttributeChanged(HTMLInputElement*); void removeButton(HTMLInputElement*); HTMLInputElement* checkedButtonForGroup(const AtomicString& groupName) const; + bool hasCheckedButton(const HTMLInputElement*) const; bool isInRequiredGroup(HTMLInputElement*) const; + Vector<HTMLInputElement*> groupMembers(const HTMLInputElement&) const; private: - typedef HashMap<AtomicStringImpl*, OwnPtr<RadioButtonGroup>> NameToGroupMap; - OwnPtr<NameToGroupMap> m_nameToGroupMap; + typedef HashMap<AtomicStringImpl*, std::unique_ptr<RadioButtonGroup>> NameToGroupMap; + std::unique_ptr<NameToGroupMap> m_nameToGroupMap; }; } // namespace WebCore - -#endif // CheckedRadioButtons_h diff --git a/Source/WebCore/dom/Range.cpp b/Source/WebCore/dom/Range.cpp index 226f63271..e0ed74888 100644 --- a/Source/WebCore/dom/Range.cpp +++ b/Source/WebCore/dom/Range.cpp @@ -27,16 +27,20 @@ #include "ClientRect.h" #include "ClientRectList.h" +#include "Comment.h" #include "DocumentFragment.h" +#include "Event.h" +#include "ExceptionCode.h" #include "Frame.h" #include "FrameView.h" +#include "HTMLBodyElement.h" +#include "HTMLDocument.h" #include "HTMLElement.h" +#include "HTMLHtmlElement.h" #include "HTMLNames.h" #include "NodeTraversal.h" #include "NodeWithIndex.h" -#include "Page.h" #include "ProcessingInstruction.h" -#include "RangeException.h" #include "RenderBoxModelObject.h" #include "RenderText.h" #include "ScopedEventQueue.h" @@ -60,6 +64,13 @@ using namespace HTMLNames; DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, rangeCounter, ("Range")); +enum ContentsProcessDirection { ProcessContentsForward, ProcessContentsBackward }; +enum class CoordinateSpace { Absolute, Client }; + +static ExceptionOr<void> processNodes(Range::ActionType, Vector<Ref<Node>>&, Node* oldContainer, RefPtr<Node> newContainer); +static ExceptionOr<RefPtr<Node>> processContentsBetweenOffsets(Range::ActionType, RefPtr<DocumentFragment>, RefPtr<Node> container, unsigned startOffset, unsigned endOffset); +static ExceptionOr<RefPtr<Node>> processAncestorsAndTheirSiblings(Range::ActionType, Node* container, ContentsProcessDirection, ExceptionOr<RefPtr<Node>>&& passedClonedContainer, Node* commonRoot); + inline Range::Range(Document& ownerDocument) : m_ownerDocument(ownerDocument) , m_start(&ownerDocument) @@ -72,12 +83,12 @@ inline Range::Range(Document& ownerDocument) m_ownerDocument->attachRange(this); } -PassRefPtr<Range> Range::create(Document& ownerDocument) +Ref<Range> Range::create(Document& ownerDocument) { - return adoptRef(new Range(ownerDocument)); + return adoptRef(*new Range(ownerDocument)); } -inline Range::Range(Document& ownerDocument, PassRefPtr<Node> startContainer, int startOffset, PassRefPtr<Node> endContainer, int endOffset) +inline Range::Range(Document& ownerDocument, Node* startContainer, int startOffset, Node* endContainer, int endOffset) : m_ownerDocument(ownerDocument) , m_start(&ownerDocument) , m_end(&ownerDocument) @@ -90,37 +101,31 @@ inline Range::Range(Document& ownerDocument, PassRefPtr<Node> startContainer, in // Simply setting the containers and offsets directly would not do any of the checking // that setStart and setEnd do, so we call those functions. - setStart(startContainer, startOffset); - setEnd(endContainer, endOffset); -} - -PassRefPtr<Range> Range::create(Document& ownerDocument, PassRefPtr<Node> startContainer, int startOffset, PassRefPtr<Node> endContainer, int endOffset) -{ - return adoptRef(new Range(ownerDocument, startContainer, startOffset, endContainer, endOffset)); + if (startContainer) + setStart(*startContainer, startOffset); + if (endContainer) + setEnd(*endContainer, endOffset); } -PassRefPtr<Range> Range::create(Document& ownerDocument, const Position& start, const Position& end) +Ref<Range> Range::create(Document& ownerDocument, RefPtr<Node>&& startContainer, int startOffset, RefPtr<Node>&& endContainer, int endOffset) { - return adoptRef(new Range(ownerDocument, start.containerNode(), start.computeOffsetInContainerNode(), end.containerNode(), end.computeOffsetInContainerNode())); + return adoptRef(*new Range(ownerDocument, startContainer.get(), startOffset, endContainer.get(), endOffset)); } -PassRefPtr<Range> Range::create(ScriptExecutionContext& context) +Ref<Range> Range::create(Document& ownerDocument, const Position& start, const Position& end) { - return adoptRef(new Range(toDocument(context))); + return adoptRef(*new Range(ownerDocument, start.containerNode(), start.computeOffsetInContainerNode(), end.containerNode(), end.computeOffsetInContainerNode())); } -#if PLATFORM(IOS) -PassRefPtr<Range> Range::create(Document& ownerDocument, const VisiblePosition& visibleStart, const VisiblePosition& visibleEnd) +Ref<Range> Range::create(Document& ownerDocument, const VisiblePosition& visibleStart, const VisiblePosition& visibleEnd) { Position start = visibleStart.deepEquivalent().parentAnchoredEquivalent(); Position end = visibleEnd.deepEquivalent().parentAnchoredEquivalent(); - return adoptRef(new Range(ownerDocument, start.anchorNode(), start.deprecatedEditingOffset(), end.anchorNode(), end.deprecatedEditingOffset())); + return adoptRef(*new Range(ownerDocument, start.anchorNode(), start.deprecatedEditingOffset(), end.anchorNode(), end.deprecatedEditingOffset())); } -#endif Range::~Range() { - // Always detach (even if we've already detached) to fix https://bugs.webkit.org/show_bug.cgi?id=26044 m_ownerDocument->detachRange(this); #ifndef NDEBUG @@ -130,64 +135,14 @@ Range::~Range() void Range::setDocument(Document& document) { - ASSERT(&m_ownerDocument.get() != &document); + ASSERT(m_ownerDocument.ptr() != &document); m_ownerDocument->detachRange(this); m_ownerDocument = document; - m_start.setToStartOfNode(&document); - m_end.setToStartOfNode(&document); + m_start.setToStartOfNode(document); + m_end.setToStartOfNode(document); m_ownerDocument->attachRange(this); } -Node* Range::startContainer(ExceptionCode& ec) const -{ - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return 0; - } - - return m_start.container(); -} - -int Range::startOffset(ExceptionCode& ec) const -{ - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return 0; - } - - return m_start.offset(); -} - -Node* Range::endContainer(ExceptionCode& ec) const -{ - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return 0; - } - - return m_end.container(); -} - -int Range::endOffset(ExceptionCode& ec) const -{ - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return 0; - } - - return m_end.offset(); -} - -Node* Range::commonAncestorContainer(ExceptionCode& ec) const -{ - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return 0; - } - - return commonAncestorContainer(m_start.container(), m_end.container()); -} - Node* Range::commonAncestorContainer(Node* containerA, Node* containerB) { for (Node* parentA = containerA; parentA; parentA = parentA->parentNode()) { @@ -196,17 +151,7 @@ Node* Range::commonAncestorContainer(Node* containerA, Node* containerB) return parentA; } } - return 0; -} - -bool Range::collapsed(ExceptionCode& ec) const -{ - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return 0; - } - - return m_start == m_end; + return nullptr; } static inline bool checkForDifferentRootContainer(const RangeBoundaryPoint& start, const RangeBoundaryPoint& end) @@ -218,257 +163,210 @@ static inline bool checkForDifferentRootContainer(const RangeBoundaryPoint& star while (startRootContainer->parentNode()) startRootContainer = startRootContainer->parentNode(); - return startRootContainer != endRootContainer || (Range::compareBoundaryPoints(start, end, ASSERT_NO_EXCEPTION) > 0); + return startRootContainer != endRootContainer || Range::compareBoundaryPoints(start, end).releaseReturnValue() > 0; } -void Range::setStart(PassRefPtr<Node> refNode, int offset, ExceptionCode& ec) +ExceptionOr<void> Range::setStart(Ref<Node>&& refNode, unsigned offset) { - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return; - } - - if (!refNode) { - ec = NOT_FOUND_ERR; - return; - } - bool didMoveDocument = false; if (&refNode->document() != &ownerDocument()) { setDocument(refNode->document()); didMoveDocument = true; } - ec = 0; - Node* childNode = checkNodeWOffset(refNode.get(), offset, ec); - if (ec) - return; + auto childNode = checkNodeWOffset(refNode, offset); + if (childNode.hasException()) + return childNode.releaseException(); - m_start.set(refNode, offset, childNode); + m_start.set(WTFMove(refNode), offset, childNode.releaseReturnValue()); if (didMoveDocument || checkForDifferentRootContainer(m_start, m_end)) - collapse(true, ec); + collapse(true); + + return { }; } -void Range::setEnd(PassRefPtr<Node> refNode, int offset, ExceptionCode& ec) +ExceptionOr<void> Range::setEnd(Ref<Node>&& refNode, unsigned offset) { - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return; - } - - if (!refNode) { - ec = NOT_FOUND_ERR; - return; - } - bool didMoveDocument = false; if (&refNode->document() != &ownerDocument()) { setDocument(refNode->document()); didMoveDocument = true; } - ec = 0; - Node* childNode = checkNodeWOffset(refNode.get(), offset, ec); - if (ec) - return; + auto childNode = checkNodeWOffset(refNode, offset); + if (childNode.hasException()) + return childNode.releaseException(); - m_end.set(refNode, offset, childNode); + m_end.set(WTFMove(refNode), offset, childNode.releaseReturnValue()); if (didMoveDocument || checkForDifferentRootContainer(m_start, m_end)) - collapse(false, ec); + collapse(false); + + return { }; } -void Range::setStart(const Position& start, ExceptionCode& ec) +ExceptionOr<void> Range::setStart(const Position& start) { Position parentAnchored = start.parentAnchoredEquivalent(); - setStart(parentAnchored.containerNode(), parentAnchored.offsetInContainerNode(), ec); + if (!parentAnchored.containerNode()) + return Exception { TypeError }; + return setStart(*parentAnchored.containerNode(), parentAnchored.offsetInContainerNode()); } -void Range::setEnd(const Position& end, ExceptionCode& ec) +ExceptionOr<void> Range::setEnd(const Position& end) { Position parentAnchored = end.parentAnchoredEquivalent(); - setEnd(parentAnchored.containerNode(), parentAnchored.offsetInContainerNode(), ec); + if (!parentAnchored.containerNode()) + return Exception { TypeError }; + return setEnd(*parentAnchored.containerNode(), parentAnchored.offsetInContainerNode()); } -void Range::collapse(bool toStart, ExceptionCode& ec) +void Range::collapse(bool toStart) { - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return; - } - if (toStart) m_end = m_start; else m_start = m_end; } -bool Range::isPointInRange(Node* refNode, int offset, ExceptionCode& ec) +ExceptionOr<bool> Range::isPointInRange(Node& refNode, unsigned offset) { - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return false; - } - - if (!refNode) { - ec = HIERARCHY_REQUEST_ERR; + if (&refNode.document() != &ownerDocument()) return false; - } - if (!refNode->inDocument() || &refNode->document() != &ownerDocument()) { - return false; + auto checkNodeResult = checkNodeWOffset(refNode, offset); + if (checkNodeResult.hasException()) { + // DOM4 spec requires us to check whether refNode and start container have the same root first + // but we do it in the reverse order to avoid O(n) operation here in common case. + if (!commonAncestorContainer(&refNode, &startContainer())) + return false; + return checkNodeResult.releaseException(); } - ec = 0; - checkNodeWOffset(refNode, offset, ec); - if (ec) + auto startCompareResult = compareBoundaryPoints(&refNode, offset, &startContainer(), m_start.offset()); + if (!(!startCompareResult.hasException() && startCompareResult.releaseReturnValue() >= 0)) return false; - - return compareBoundaryPoints(refNode, offset, m_start.container(), m_start.offset(), ec) >= 0 && !ec - && compareBoundaryPoints(refNode, offset, m_end.container(), m_end.offset(), ec) <= 0 && !ec; + auto endCompareResult = compareBoundaryPoints(&refNode, offset, &endContainer(), m_end.offset()); + return !endCompareResult.hasException() && endCompareResult.releaseReturnValue() <= 0; } -short Range::comparePoint(Node* refNode, int offset, ExceptionCode& ec) const +ExceptionOr<short> Range::comparePoint(Node& refNode, unsigned offset) const { // http://developer.mozilla.org/en/docs/DOM:range.comparePoint // This method returns -1, 0 or 1 depending on if the point described by the // refNode node and an offset within the node is before, same as, or after the range respectively. + if (&refNode.document() != &ownerDocument()) + return Exception { WRONG_DOCUMENT_ERR }; - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return 0; - } - - if (!refNode) { - ec = HIERARCHY_REQUEST_ERR; - return 0; - } - - if (!refNode->inDocument() || &refNode->document() != &ownerDocument()) { - ec = WRONG_DOCUMENT_ERR; - return 0; + auto checkNodeResult = checkNodeWOffset(refNode, offset); + if (checkNodeResult.hasException()) { + // DOM4 spec requires us to check whether refNode and start container have the same root first + // but we do it in the reverse order to avoid O(n) operation here in common case. + if (!refNode.isConnected() && !commonAncestorContainer(&refNode, &startContainer())) + return Exception { WRONG_DOCUMENT_ERR }; + return checkNodeResult.releaseException(); } - ec = 0; - checkNodeWOffset(refNode, offset, ec); - if (ec) - return 0; - // compare to start, and point comes before - if (compareBoundaryPoints(refNode, offset, m_start.container(), m_start.offset(), ec) < 0) + auto startCompareResult = compareBoundaryPoints(&refNode, offset, &startContainer(), m_start.offset()); + if (startCompareResult.hasException()) + return startCompareResult.releaseException(); + if (startCompareResult.releaseReturnValue() < 0) return -1; - if (ec) - return 0; - // compare to end, and point comes after - if (compareBoundaryPoints(refNode, offset, m_end.container(), m_end.offset(), ec) > 0 && !ec) + auto endCompareResult = compareBoundaryPoints(&refNode, offset, &endContainer(), m_end.offset()); + if (endCompareResult.hasException()) + return endCompareResult.releaseException(); + if (endCompareResult.releaseReturnValue() > 0) return 1; // point is in the middle of this range, or on the boundary points return 0; } -Range::CompareResults Range::compareNode(Node* refNode, ExceptionCode& ec) const +ExceptionOr<Range::CompareResults> Range::compareNode(Node& refNode) const { // http://developer.mozilla.org/en/docs/DOM:range.compareNode // This method returns 0, 1, 2, or 3 based on if the node is before, after, // before and after(surrounds), or inside the range, respectively - if (!refNode) { - ec = NOT_FOUND_ERR; - return NODE_BEFORE; - } - - if (!m_start.container() && refNode->inDocument()) { - ec = INVALID_STATE_ERR; - return NODE_BEFORE; - } - - if (m_start.container() && !refNode->inDocument()) { + if (!refNode.isConnected()) { // Firefox doesn't throw an exception for this case; it returns 0. return NODE_BEFORE; } - if (&refNode->document() != &ownerDocument()) { + if (&refNode.document() != &ownerDocument()) { // Firefox doesn't throw an exception for this case; it returns 0. return NODE_BEFORE; } - ContainerNode* parentNode = refNode->parentNode(); - int nodeIndex = refNode->nodeIndex(); - + auto* parentNode = refNode.parentNode(); if (!parentNode) { - // if the node is the top document we should return NODE_BEFORE_AND_AFTER - // but we throw to match firefox behavior - ec = NOT_FOUND_ERR; - return NODE_BEFORE; + // If the node is the top of the tree we should return NODE_BEFORE_AND_AFTER, + // but we throw to match firefox behavior. + return Exception { NOT_FOUND_ERR }; } + auto nodeIndex = refNode.computeNodeIndex(); - if (comparePoint(parentNode, nodeIndex, ec) < 0) { // starts before - if (comparePoint(parentNode, nodeIndex + 1, ec) > 0) // ends after the range - return NODE_BEFORE_AND_AFTER; - return NODE_BEFORE; // ends before or in the range - } else { // starts at or after the range start - if (comparePoint(parentNode, nodeIndex + 1, ec) > 0) // ends after the range - return NODE_AFTER; - return NODE_INSIDE; // ends inside the range - } + auto nodeStartCompareResult = comparePoint(*parentNode, nodeIndex); + if (nodeStartCompareResult.hasException()) + return nodeStartCompareResult.releaseException(); + auto nodeEndCompareResult = comparePoint(*parentNode, nodeIndex + 1); + if (nodeEndCompareResult.hasException()) + return nodeEndCompareResult.releaseException(); + + bool nodeStartsBeforeRange = nodeStartCompareResult.releaseReturnValue() < 0; + bool nodeEndsAfterRange = nodeEndCompareResult.releaseReturnValue() > 0; + + return nodeStartsBeforeRange + ? (nodeEndsAfterRange ? NODE_BEFORE_AND_AFTER : NODE_BEFORE) + : (nodeEndsAfterRange ? NODE_AFTER : NODE_INSIDE); } -short Range::compareBoundaryPoints(CompareHow how, const Range* sourceRange, ExceptionCode& ec) const +static inline Node* top(Node& node) { - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return 0; - } - - if (!sourceRange) { - ec = NOT_FOUND_ERR; - return 0; - } + auto* top = &node; + while (auto* parent = top->parentNode()) + top = parent; + return top; +} - ec = 0; - Node* thisCont = commonAncestorContainer(ec); - if (ec) - return 0; - Node* sourceCont = sourceRange->commonAncestorContainer(ec); - if (ec) - return 0; +ExceptionOr<short> Range::compareBoundaryPoints(CompareHow how, const Range& sourceRange) const +{ + auto* thisContainer = commonAncestorContainer(); + auto* sourceContainer = sourceRange.commonAncestorContainer(); + if (!thisContainer || !sourceContainer || &thisContainer->document() != &sourceContainer->document() || top(*thisContainer) != top(*sourceContainer)) + return Exception { WRONG_DOCUMENT_ERR }; - if (&thisCont->document() != &sourceCont->document()) { - ec = WRONG_DOCUMENT_ERR; - return 0; + switch (how) { + case START_TO_START: + return compareBoundaryPoints(m_start, sourceRange.m_start); + case START_TO_END: + return compareBoundaryPoints(m_end, sourceRange.m_start); + case END_TO_END: + return compareBoundaryPoints(m_end, sourceRange.m_end); + case END_TO_START: + return compareBoundaryPoints(m_start, sourceRange.m_end); } - Node* thisTop = thisCont; - Node* sourceTop = sourceCont; - while (thisTop->parentNode()) - thisTop = thisTop->parentNode(); - while (sourceTop->parentNode()) - sourceTop = sourceTop->parentNode(); - if (thisTop != sourceTop) { // in different DocumentFragments - ec = WRONG_DOCUMENT_ERR; - return 0; - } + return Exception { SYNTAX_ERR }; +} +ExceptionOr<short> Range::compareBoundaryPointsForBindings(unsigned short how, const Range& sourceRange) const +{ switch (how) { - case START_TO_START: - return compareBoundaryPoints(m_start, sourceRange->m_start, ec); - case START_TO_END: - return compareBoundaryPoints(m_end, sourceRange->m_start, ec); - case END_TO_END: - return compareBoundaryPoints(m_end, sourceRange->m_end, ec); - case END_TO_START: - return compareBoundaryPoints(m_start, sourceRange->m_end, ec); + case START_TO_START: + case START_TO_END: + case END_TO_END: + case END_TO_START: + return compareBoundaryPoints(static_cast<CompareHow>(how), sourceRange); } - - ec = SYNTAX_ERR; - return 0; + return Exception { NOT_SUPPORTED_ERR }; } -short Range::compareBoundaryPoints(Node* containerA, int offsetA, Node* containerB, int offsetB, ExceptionCode& ec) +ExceptionOr<short> Range::compareBoundaryPoints(Node* containerA, unsigned offsetA, Node* containerB, unsigned offsetB) { ASSERT(containerA); ASSERT(containerB); @@ -483,11 +381,10 @@ short Range::compareBoundaryPoints(Node* containerA, int offsetA, Node* containe // case 1: both points have the same container if (containerA == containerB) { if (offsetA == offsetB) - return 0; // A is equal to B + return 0; // A is equal to B if (offsetA < offsetB) - return -1; // A is before B - else - return 1; // A is after B + return -1; // A is before B + return 1; // A is after B } // case 2: node C (container B or an ancestor) is a child node of A @@ -495,17 +392,15 @@ short Range::compareBoundaryPoints(Node* containerA, int offsetA, Node* containe while (c && c->parentNode() != containerA) c = c->parentNode(); if (c) { - int offsetC = 0; + unsigned offsetC = 0; Node* n = containerA->firstChild(); while (n != c && offsetC < offsetA) { offsetC++; n = n->nextSibling(); } - if (offsetA <= offsetC) - return -1; // A is before B - else - return 1; // A is after B + return -1; // A is before B + return 1; // A is after B } // case 3: node C (container A or an ancestor) is a child node of B @@ -513,26 +408,22 @@ short Range::compareBoundaryPoints(Node* containerA, int offsetA, Node* containe while (c && c->parentNode() != containerB) c = c->parentNode(); if (c) { - int offsetC = 0; + unsigned offsetC = 0; Node* n = containerB->firstChild(); while (n != c && offsetC < offsetB) { offsetC++; n = n->nextSibling(); } - if (offsetC < offsetB) - return -1; // A is before B - else - return 1; // A is after B + return -1; // A is before B + return 1; // A is after B } // case 4: containers A & B are siblings, or children of siblings // ### we need to do a traversal here instead - Node* commonAncestor = commonAncestorContainer(containerA, containerB); - if (!commonAncestor) { - ec = WRONG_DOCUMENT_ERR; - return 0; - } + auto* commonAncestor = commonAncestorContainer(containerA, containerB); + if (!commonAncestor) + return Exception { WRONG_DOCUMENT_ERR }; Node* childA = containerA; while (childA && childA->parentNode() != commonAncestor) childA = childA->parentNode(); @@ -561,65 +452,51 @@ short Range::compareBoundaryPoints(Node* containerA, int offsetA, Node* containe return 0; } -short Range::compareBoundaryPoints(const RangeBoundaryPoint& boundaryA, const RangeBoundaryPoint& boundaryB, ExceptionCode& ec) +ExceptionOr<short> Range::compareBoundaryPoints(const RangeBoundaryPoint& boundaryA, const RangeBoundaryPoint& boundaryB) { - return compareBoundaryPoints(boundaryA.container(), boundaryA.offset(), boundaryB.container(), boundaryB.offset(), ec); + return compareBoundaryPoints(boundaryA.container(), boundaryA.offset(), boundaryB.container(), boundaryB.offset()); } bool Range::boundaryPointsValid() const { - ExceptionCode ec = 0; - return m_start.container() && compareBoundaryPoints(m_start, m_end, ec) <= 0 && !ec; + auto result = compareBoundaryPoints(m_start, m_end); + return !result.hasException() && result.releaseReturnValue() <= 0; } -void Range::deleteContents(ExceptionCode& ec) +ExceptionOr<void> Range::deleteContents() { - checkDeleteExtract(ec); - if (ec) - return; - - processContents(Delete, ec); + auto result = processContents(Delete); + if (result.hasException()) + return result.releaseException(); + return { }; } -bool Range::intersectsNode(Node* refNode, ExceptionCode& ec) +ExceptionOr<bool> Range::intersectsNode(Node& refNode) const { - // http://developer.mozilla.org/en/docs/DOM:range.intersectsNode - // Returns a bool if the node intersects the range. - - // Throw exception if the range is already detached. - if (!m_start.container()) { - ec = INVALID_STATE_ERR; + if (!refNode.isConnected() || &refNode.document() != &ownerDocument()) return false; - } - if (!refNode) { - ec = NOT_FOUND_ERR; - return false; - } - if (!refNode->inDocument() || &refNode->document() != &ownerDocument()) { - // Firefox doesn't throw an exception for these cases; it returns false. - return false; - } + ContainerNode* parentNode = refNode.parentNode(); + if (!parentNode) + return true; - ContainerNode* parentNode = refNode->parentNode(); - int nodeIndex = refNode->nodeIndex(); - - if (!parentNode) { - // if the node is the top document we should return NODE_BEFORE_AND_AFTER - // but we throw to match firefox behavior - ec = NOT_FOUND_ERR; - return false; - } + unsigned nodeIndex = refNode.computeNodeIndex(); - if (comparePoint(parentNode, nodeIndex, ec) < 0 && // starts before start - comparePoint(parentNode, nodeIndex + 1, ec) < 0) { // ends before start - return false; - } else if (comparePoint(parentNode, nodeIndex, ec) > 0 && // starts after end - comparePoint(parentNode, nodeIndex + 1, ec) > 0) { // ends after end - return false; - } - - return true; // all other cases + // If (parent, offset) is before end and (parent, offset + 1) is after start, return true. + // Otherwise, return false. + auto result = comparePoint(*parentNode, nodeIndex); + if (result.hasException()) + return result.releaseException(); + auto compareFirst = result.releaseReturnValue(); + result = comparePoint(*parentNode, nodeIndex + 1); + if (result.hasException()) + return result.releaseException(); + auto compareSecond = result.releaseReturnValue(); + + bool isFirstBeforeEnd = m_start == m_end ? compareFirst < 0 : compareFirst <= 0; + bool isSecondAfterStart = m_start == m_end ? compareSecond > 0 : compareSecond >= 0; + + return isFirstBeforeEnd && isSecondAfterStart; } static inline Node* highestAncestorUnderCommonRoot(Node* node, Node* commonRoot) @@ -655,53 +532,44 @@ static inline Node* childOfCommonRootBeforeOffset(Node* container, unsigned offs return container; } -static inline unsigned lengthOfContentsInNode(Node* node) +static inline unsigned lengthOfContentsInNode(Node& node) { // This switch statement must be consistent with that of Range::processContentsBetweenOffsets. - switch (node->nodeType()) { + switch (node.nodeType()) { + case Node::DOCUMENT_TYPE_NODE: + return 0; case Node::TEXT_NODE: case Node::CDATA_SECTION_NODE: case Node::COMMENT_NODE: - return toCharacterData(node)->length(); case Node::PROCESSING_INSTRUCTION_NODE: - return toProcessingInstruction(node)->data().length(); + return downcast<CharacterData>(node).length(); case Node::ELEMENT_NODE: case Node::ATTRIBUTE_NODE: - case Node::ENTITY_REFERENCE_NODE: - case Node::ENTITY_NODE: case Node::DOCUMENT_NODE: - case Node::DOCUMENT_TYPE_NODE: case Node::DOCUMENT_FRAGMENT_NODE: - case Node::NOTATION_NODE: - case Node::XPATH_NAMESPACE_NODE: - return node->childNodeCount(); + return downcast<ContainerNode>(node).countChildNodes(); } ASSERT_NOT_REACHED(); return 0; } -PassRefPtr<DocumentFragment> Range::processContents(ActionType action, ExceptionCode& ec) +ExceptionOr<RefPtr<DocumentFragment>> Range::processContents(ActionType action) { - typedef Vector<RefPtr<Node>> NodeVector; - RefPtr<DocumentFragment> fragment; if (action == Extract || action == Clone) fragment = DocumentFragment::create(ownerDocument()); - ec = 0; - if (collapsed(ec)) - return fragment.release(); - if (ec) - return 0; + if (collapsed()) + return WTFMove(fragment); - RefPtr<Node> commonRoot = commonAncestorContainer(ec); - if (ec) - return 0; + RefPtr<Node> commonRoot = commonAncestorContainer(); ASSERT(commonRoot); - if (m_start.container() == m_end.container()) { - processContentsBetweenOffsets(action, fragment, m_start.container(), m_start.offset(), m_end.offset(), ec); - return fragment; + if (&startContainer() == &endContainer()) { + auto result = processContentsBetweenOffsets(action, fragment, &startContainer(), m_start.offset(), m_end.offset()); + if (result.hasException()) + return result.releaseException(); + return WTFMove(fragment); } // Since mutation events can modify the range during the process, the boundary points need to be saved. @@ -734,14 +602,20 @@ PassRefPtr<DocumentFragment> Range::processContents(ActionType action, Exception RefPtr<Node> leftContents; if (originalStart.container() != commonRoot && commonRoot->contains(originalStart.container())) { - leftContents = processContentsBetweenOffsets(action, 0, originalStart.container(), originalStart.offset(), lengthOfContentsInNode(originalStart.container()), ec); - leftContents = processAncestorsAndTheirSiblings(action, originalStart.container(), ProcessContentsForward, leftContents, commonRoot.get(), ec); + auto firstResult = processContentsBetweenOffsets(action, nullptr, originalStart.container(), originalStart.offset(), lengthOfContentsInNode(*originalStart.container())); + auto secondResult = processAncestorsAndTheirSiblings(action, originalStart.container(), ProcessContentsForward, WTFMove(firstResult), commonRoot.get()); + // FIXME: A bit peculiar that we silently ignore the exception here, but we do have at least some regression tests that rely on this behavior. + if (!secondResult.hasException()) + leftContents = secondResult.releaseReturnValue(); } RefPtr<Node> rightContents; - if (m_end.container() != commonRoot && commonRoot->contains(originalEnd.container())) { - rightContents = processContentsBetweenOffsets(action, 0, originalEnd.container(), 0, originalEnd.offset(), ec); - rightContents = processAncestorsAndTheirSiblings(action, originalEnd.container(), ProcessContentsBackward, rightContents, commonRoot.get(), ec); + if (&endContainer() != commonRoot && commonRoot->contains(originalEnd.container())) { + auto firstResult = processContentsBetweenOffsets(action, nullptr, originalEnd.container(), 0, originalEnd.offset()); + auto secondResult = processAncestorsAndTheirSiblings(action, originalEnd.container(), ProcessContentsBackward, WTFMove(firstResult), commonRoot.get()); + // FIXME: A bit peculiar that we silently ignore the exception here, but we do have at least some regression tests that rely on this behavior. + if (!secondResult.hasException()) + rightContents = secondResult.releaseReturnValue(); } // delete all children of commonRoot between the start and end container @@ -752,342 +626,326 @@ PassRefPtr<DocumentFragment> Range::processContents(ActionType action, Exception // Collapse the range, making sure that the result is not within a node that was partially selected. if (action == Extract || action == Delete) { - if (partialStart && commonRoot->contains(partialStart.get())) - setStart(partialStart->parentNode(), partialStart->nodeIndex() + 1, ec); - else if (partialEnd && commonRoot->contains(partialEnd.get())) - setStart(partialEnd->parentNode(), partialEnd->nodeIndex(), ec); - if (ec) - return 0; + if (partialStart && commonRoot->contains(partialStart.get())) { + auto result = setStart(*partialStart->parentNode(), partialStart->computeNodeIndex() + 1); + if (result.hasException()) + return result.releaseException(); + } else if (partialEnd && commonRoot->contains(partialEnd.get())) { + auto result = setStart(*partialEnd->parentNode(), partialEnd->computeNodeIndex()); + if (result.hasException()) + return result.releaseException(); + } m_end = m_start; } // Now add leftContents, stuff in between, and rightContents to the fragment // (or just delete the stuff in between) - if ((action == Extract || action == Clone) && leftContents) - fragment->appendChild(leftContents, ec); + if ((action == Extract || action == Clone) && leftContents) { + auto result = fragment->appendChild(*leftContents); + if (result.hasException()) + return result.releaseException(); + } if (processStart) { - NodeVector nodes; - for (Node* n = processStart.get(); n && n != processEnd; n = n->nextSibling()) - nodes.append(n); - processNodes(action, nodes, commonRoot, fragment, ec); + Vector<Ref<Node>> nodes; + for (Node* node = processStart.get(); node && node != processEnd; node = node->nextSibling()) + nodes.append(*node); + auto result = processNodes(action, nodes, commonRoot.get(), fragment.get()); + if (result.hasException()) + return result.releaseException(); } - if ((action == Extract || action == Clone) && rightContents) - fragment->appendChild(rightContents, ec); + if ((action == Extract || action == Clone) && rightContents) { + auto result = fragment->appendChild(*rightContents); + if (result.hasException()) + return result.releaseException(); + } - return fragment.release(); + return WTFMove(fragment); } -static inline void deleteCharacterData(PassRefPtr<CharacterData> data, unsigned startOffset, unsigned endOffset, ExceptionCode& ec) +static inline ExceptionOr<void> deleteCharacterData(CharacterData& data, unsigned startOffset, unsigned endOffset) { - if (data->length() - endOffset) - data->deleteData(endOffset, data->length() - endOffset, ec); - if (startOffset) - data->deleteData(0, startOffset, ec); + if (data.length() - endOffset) { + auto result = data.deleteData(endOffset, data.length() - endOffset); + if (result.hasException()) + return result.releaseException(); + } + if (startOffset) { + auto result = data.deleteData(0, startOffset); + if (result.hasException()) + return result.releaseException(); + } + return { }; } -PassRefPtr<Node> Range::processContentsBetweenOffsets(ActionType action, PassRefPtr<DocumentFragment> fragment, Node* container, unsigned startOffset, unsigned endOffset, ExceptionCode& ec) +static ExceptionOr<RefPtr<Node>> processContentsBetweenOffsets(Range::ActionType action, RefPtr<DocumentFragment> fragment, RefPtr<Node> container, unsigned startOffset, unsigned endOffset) { ASSERT(container); ASSERT(startOffset <= endOffset); + RefPtr<Node> result; + // This switch statement must be consistent with that of lengthOfContentsInNode. - RefPtr<Node> result; switch (container->nodeType()) { case Node::TEXT_NODE: case Node::CDATA_SECTION_NODE: case Node::COMMENT_NODE: - endOffset = std::min(endOffset, static_cast<CharacterData*>(container)->length()); + endOffset = std::min(endOffset, downcast<CharacterData>(*container).length()); startOffset = std::min(startOffset, endOffset); - if (action == Extract || action == Clone) { - RefPtr<CharacterData> c = static_pointer_cast<CharacterData>(container->cloneNode(true)); - deleteCharacterData(c, startOffset, endOffset, ec); + if (action == Range::Extract || action == Range::Clone) { + Ref<CharacterData> characters = downcast<CharacterData>(container->cloneNode(true).get()); + auto deleteResult = deleteCharacterData(characters, startOffset, endOffset); + if (deleteResult.hasException()) + return deleteResult.releaseException(); if (fragment) { result = fragment; - result->appendChild(c.release(), ec); + auto appendResult = result->appendChild(characters); + if (appendResult.hasException()) + return appendResult.releaseException(); } else - result = c.release(); + result = WTFMove(characters); + } + if (action == Range::Extract || action == Range::Delete) { + auto deleteResult = downcast<CharacterData>(*container).deleteData(startOffset, endOffset - startOffset); + if (deleteResult.hasException()) + return deleteResult.releaseException(); } - if (action == Extract || action == Delete) - toCharacterData(container)->deleteData(startOffset, endOffset - startOffset, ec); break; - case Node::PROCESSING_INSTRUCTION_NODE: - endOffset = std::min(endOffset, static_cast<ProcessingInstruction*>(container)->data().length()); + case Node::PROCESSING_INSTRUCTION_NODE: { + auto& instruction = downcast<ProcessingInstruction>(*container); + endOffset = std::min(endOffset, downcast<ProcessingInstruction>(*container).data().length()); startOffset = std::min(startOffset, endOffset); - if (action == Extract || action == Clone) { - RefPtr<ProcessingInstruction> c = static_pointer_cast<ProcessingInstruction>(container->cloneNode(true)); - c->setData(c->data().substring(startOffset, endOffset - startOffset), ec); + if (action == Range::Extract || action == Range::Clone) { + Ref<ProcessingInstruction> processingInstruction = downcast<ProcessingInstruction>(container->cloneNode(true).get()); + processingInstruction->setData(processingInstruction->data().substring(startOffset, endOffset - startOffset)); if (fragment) { result = fragment; - result->appendChild(c.release(), ec); + auto appendResult = result->appendChild(processingInstruction); + if (appendResult.hasException()) + return appendResult.releaseException(); } else - result = c.release(); + result = WTFMove(processingInstruction); } - if (action == Extract || action == Delete) { - ProcessingInstruction* pi = toProcessingInstruction(container); - String data(pi->data()); + if (action == Range::Extract || action == Range::Delete) { + String data { instruction.data() }; data.remove(startOffset, endOffset - startOffset); - pi->setData(data, ec); + instruction.setData(data); } break; + } case Node::ELEMENT_NODE: case Node::ATTRIBUTE_NODE: - case Node::ENTITY_REFERENCE_NODE: - case Node::ENTITY_NODE: case Node::DOCUMENT_NODE: case Node::DOCUMENT_TYPE_NODE: case Node::DOCUMENT_FRAGMENT_NODE: - case Node::NOTATION_NODE: - case Node::XPATH_NAMESPACE_NODE: // FIXME: Should we assert that some nodes never appear here? - if (action == Extract || action == Clone) { + if (action == Range::Extract || action == Range::Clone) { if (fragment) result = fragment; else result = container->cloneNode(false); } - + Vector<Ref<Node>> nodes; Node* n = container->firstChild(); - Vector<RefPtr<Node>> nodes; for (unsigned i = startOffset; n && i; i--) n = n->nextSibling(); - for (unsigned i = startOffset; n && i < endOffset; i++, n = n->nextSibling()) - nodes.append(n); - - processNodes(action, nodes, container, result, ec); + for (unsigned i = startOffset; n && i < endOffset; i++, n = n->nextSibling()) { + if (action != Range::Delete && n->isDocumentTypeNode()) { + return Exception { HIERARCHY_REQUEST_ERR }; + } + nodes.append(*n); + } + auto processResult = processNodes(action, nodes, container.get(), result); + if (processResult.hasException()) + return processResult.releaseException(); break; } - return result.release(); + return WTFMove(result); } -void Range::processNodes(ActionType action, Vector<RefPtr<Node>>& nodes, PassRefPtr<Node> oldContainer, PassRefPtr<Node> newContainer, ExceptionCode& ec) +static ExceptionOr<void> processNodes(Range::ActionType action, Vector<Ref<Node>>& nodes, Node* oldContainer, RefPtr<Node> newContainer) { - for (unsigned i = 0; i < nodes.size(); i++) { + for (auto& node : nodes) { switch (action) { - case Delete: - oldContainer->removeChild(nodes[i].get(), ec); + case Range::Delete: { + auto result = oldContainer->removeChild(node); + if (result.hasException()) + return result.releaseException(); break; - case Extract: - newContainer->appendChild(nodes[i].release(), ec); // will remove n from its parent + } + case Range::Extract: { + auto result = newContainer->appendChild(node); // will remove node from its parent + if (result.hasException()) + return result.releaseException(); break; - case Clone: - newContainer->appendChild(nodes[i]->cloneNode(true), ec); + } + case Range::Clone: { + auto result = newContainer->appendChild(node->cloneNode(true)); + if (result.hasException()) + return result.releaseException(); break; } + } } + return { }; } -PassRefPtr<Node> Range::processAncestorsAndTheirSiblings(ActionType action, Node* container, ContentsProcessDirection direction, PassRefPtr<Node> passedClonedContainer, Node* commonRoot, ExceptionCode& ec) +ExceptionOr<RefPtr<Node>> processAncestorsAndTheirSiblings(Range::ActionType action, Node* container, ContentsProcessDirection direction, ExceptionOr<RefPtr<Node>>&& passedClonedContainer, Node* commonRoot) { - typedef Vector<RefPtr<Node>> NodeVector; + if (passedClonedContainer.hasException()) + return WTFMove(passedClonedContainer); - RefPtr<Node> clonedContainer = passedClonedContainer; - Vector<RefPtr<Node>> ancestors; - for (ContainerNode* n = container->parentNode(); n && n != commonRoot; n = n->parentNode()) - ancestors.append(n); + RefPtr<Node> clonedContainer = passedClonedContainer.releaseReturnValue(); + + Vector<Ref<ContainerNode>> ancestors; + for (ContainerNode* ancestor = container->parentNode(); ancestor && ancestor != commonRoot; ancestor = ancestor->parentNode()) + ancestors.append(*ancestor); RefPtr<Node> firstChildInAncestorToProcess = direction == ProcessContentsForward ? container->nextSibling() : container->previousSibling(); - for (Vector<RefPtr<Node>>::const_iterator it = ancestors.begin(); it != ancestors.end(); ++it) { - RefPtr<Node> ancestor = *it; - if (action == Extract || action == Clone) { - if (RefPtr<Node> clonedAncestor = ancestor->cloneNode(false)) { // Might have been removed already during mutation event. - clonedAncestor->appendChild(clonedContainer, ec); - clonedContainer = clonedAncestor; + for (auto& ancestor : ancestors) { + if (action == Range::Extract || action == Range::Clone) { + auto clonedAncestor = ancestor->cloneNode(false); // Might have been removed already during mutation event. + if (clonedContainer) { + auto result = clonedAncestor->appendChild(*clonedContainer); + if (result.hasException()) + return result.releaseException(); } + clonedContainer = WTFMove(clonedAncestor); } // Copy siblings of an ancestor of start/end containers // FIXME: This assertion may fail if DOM is modified during mutation event // FIXME: Share code with Range::processNodes - ASSERT(!firstChildInAncestorToProcess || firstChildInAncestorToProcess->parentNode() == ancestor); + ASSERT(!firstChildInAncestorToProcess || firstChildInAncestorToProcess->parentNode() == ancestor.ptr()); - NodeVector nodes; + Vector<Ref<Node>> nodes; for (Node* child = firstChildInAncestorToProcess.get(); child; child = (direction == ProcessContentsForward) ? child->nextSibling() : child->previousSibling()) - nodes.append(child); + nodes.append(*child); - for (NodeVector::const_iterator it = nodes.begin(); it != nodes.end(); ++it) { - Node* child = it->get(); + for (auto& child : nodes) { switch (action) { - case Delete: - ancestor->removeChild(child, ec); + case Range::Delete: { + auto result = ancestor->removeChild(child); + if (result.hasException()) + return result.releaseException(); break; - case Extract: // will remove child from ancestor - if (direction == ProcessContentsForward) - clonedContainer->appendChild(child, ec); - else - clonedContainer->insertBefore(child, clonedContainer->firstChild(), ec); + } + case Range::Extract: // will remove child from ancestor + if (direction == ProcessContentsForward) { + auto result = clonedContainer->appendChild(child); + if (result.hasException()) + return result.releaseException(); + } else { + auto result = clonedContainer->insertBefore(child, clonedContainer->firstChild()); + if (result.hasException()) + return result.releaseException(); + } break; - case Clone: - if (direction == ProcessContentsForward) - clonedContainer->appendChild(child->cloneNode(true), ec); - else - clonedContainer->insertBefore(child->cloneNode(true), clonedContainer->firstChild(), ec); + case Range::Clone: + if (direction == ProcessContentsForward) { + auto result = clonedContainer->appendChild(child->cloneNode(true)); + if (result.hasException()) + return result.releaseException(); + } else { + auto result = clonedContainer->insertBefore(child->cloneNode(true), clonedContainer->firstChild()); + if (result.hasException()) + return result.releaseException(); + } break; } } firstChildInAncestorToProcess = direction == ProcessContentsForward ? ancestor->nextSibling() : ancestor->previousSibling(); } - return clonedContainer.release(); + return WTFMove(clonedContainer); } -PassRefPtr<DocumentFragment> Range::extractContents(ExceptionCode& ec) +ExceptionOr<Ref<DocumentFragment>> Range::extractContents() { - checkDeleteExtract(ec); - if (ec) - return 0; - - return processContents(Extract, ec); + auto result = processContents(Extract); + if (result.hasException()) + return result.releaseException(); + return result.releaseReturnValue().releaseNonNull(); } -PassRefPtr<DocumentFragment> Range::cloneContents(ExceptionCode& ec) +ExceptionOr<Ref<DocumentFragment>> Range::cloneContents() { - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return 0; - } - - return processContents(Clone, ec); + auto result = processContents(Clone); + if (result.hasException()) + return result.releaseException(); + return result.releaseReturnValue().releaseNonNull(); } -void Range::insertNode(PassRefPtr<Node> prpNewNode, ExceptionCode& ec) +ExceptionOr<void> Range::insertNode(Ref<Node>&& node) { - RefPtr<Node> newNode = prpNewNode; + auto startContainerNodeType = startContainer().nodeType(); - ec = 0; - - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return; - } + if (startContainerNodeType == Node::COMMENT_NODE || startContainerNodeType == Node::PROCESSING_INSTRUCTION_NODE) + return Exception { HIERARCHY_REQUEST_ERR }; + bool startIsText = startContainerNodeType == Node::TEXT_NODE; + if (startIsText && !startContainer().parentNode()) + return Exception { HIERARCHY_REQUEST_ERR }; + if (node.ptr() == &startContainer()) + return Exception { HIERARCHY_REQUEST_ERR }; - if (!newNode) { - ec = NOT_FOUND_ERR; - return; - } + RefPtr<Node> referenceNode = startIsText ? &startContainer() : startContainer().traverseToChildAt(startOffset()); + Node* parentNode = referenceNode ? referenceNode->parentNode() : &startContainer(); + if (!is<ContainerNode>(parentNode)) + return Exception { HIERARCHY_REQUEST_ERR }; - // NO_MODIFICATION_ALLOWED_ERR: Raised if an ancestor container of either boundary-point of - // the Range is read-only. - if (containedByReadOnly()) { - ec = NO_MODIFICATION_ALLOWED_ERR; - return; - } + Ref<ContainerNode> parent = downcast<ContainerNode>(*parentNode); - // HIERARCHY_REQUEST_ERR: Raised if the container of the start of the Range is of a type that - // does not allow children of the type of newNode or if newNode is an ancestor of the container. + auto result = parent->ensurePreInsertionValidity(node, referenceNode.get()); + if (result.hasException()) + return result.releaseException(); - // an extra one here - if a text node is going to split, it must have a parent to insert into - bool startIsText = m_start.container()->isTextNode(); - if (startIsText && !m_start.container()->parentNode()) { - ec = HIERARCHY_REQUEST_ERR; - return; + EventQueueScope scope; + if (startIsText) { + auto result = downcast<Text>(startContainer()).splitText(startOffset()); + if (result.hasException()) + return result.releaseException(); + referenceNode = result.releaseReturnValue(); } - // In the case where the container is a text node, we check against the container's parent, because - // text nodes get split up upon insertion. - Node* checkAgainst; - if (startIsText) - checkAgainst = m_start.container()->parentNode(); - else - checkAgainst = m_start.container(); - - Node::NodeType newNodeType = newNode->nodeType(); - int numNewChildren; - if (newNodeType == Node::DOCUMENT_FRAGMENT_NODE && !newNode->isShadowRoot()) { - // check each child node, not the DocumentFragment itself - numNewChildren = 0; - for (Node* c = newNode->firstChild(); c; c = c->nextSibling()) { - if (!checkAgainst->childTypeAllowed(c->nodeType())) { - ec = HIERARCHY_REQUEST_ERR; - return; - } - ++numNewChildren; - } - } else { - numNewChildren = 1; - if (!checkAgainst->childTypeAllowed(newNodeType)) { - ec = HIERARCHY_REQUEST_ERR; - return; - } - } + if (referenceNode == node.ptr()) + referenceNode = referenceNode->nextSibling(); - for (Node* n = m_start.container(); n; n = n->parentNode()) { - if (n == newNode) { - ec = HIERARCHY_REQUEST_ERR; - return; - } - } + auto removeResult = node->remove(); + if (removeResult.hasException()) + return removeResult.releaseException(); - // INVALID_NODE_TYPE_ERR: Raised if newNode is an Attr, Entity, Notation, ShadowRoot or Document node. - switch (newNodeType) { - case Node::ATTRIBUTE_NODE: - case Node::ENTITY_NODE: - case Node::NOTATION_NODE: - case Node::DOCUMENT_NODE: - ec = RangeException::INVALID_NODE_TYPE_ERR; - return; - default: - if (newNode->isShadowRoot()) { - ec = RangeException::INVALID_NODE_TYPE_ERR; - return; - } - break; - } + unsigned newOffset = referenceNode ? referenceNode->computeNodeIndex() : parent->countChildNodes(); + if (is<DocumentFragment>(node.get())) + newOffset += downcast<DocumentFragment>(node.get()).countChildNodes(); + else + ++newOffset; - EventQueueScope scope; - bool collapsed = m_start == m_end; - RefPtr<Node> container; - if (startIsText) { - container = m_start.container(); - RefPtr<Text> newText = toText(container.get())->splitText(m_start.offset(), ec); - if (ec) - return; - - container = m_start.container(); - container->parentNode()->insertBefore(newNode.release(), newText.get(), ec); - if (ec) - return; + auto insertResult = parent->insertBefore(node, referenceNode.get()); + if (insertResult.hasException()) + return insertResult.releaseException(); - if (collapsed && newText->parentNode() == container && &container->document() == &ownerDocument()) - m_end.setToBeforeChild(newText.get()); - } else { - container = m_start.container(); - RefPtr<Node> firstInsertedChild = newNodeType == Node::DOCUMENT_FRAGMENT_NODE ? newNode->firstChild() : newNode; - RefPtr<Node> lastInsertedChild = newNodeType == Node::DOCUMENT_FRAGMENT_NODE ? newNode->lastChild() : newNode; - RefPtr<Node> childAfterInsertedContent = container->childNode(m_start.offset()); - container->insertBefore(newNode.release(), childAfterInsertedContent.get(), ec); - if (ec) - return; + if (collapsed()) + return setEnd(WTFMove(parent), newOffset); - if (collapsed && numNewChildren && &container->document() == &ownerDocument()) { - if (firstInsertedChild->parentNode() == container) - m_start.setToBeforeChild(firstInsertedChild.get()); - if (lastInsertedChild->parentNode() == container) - m_end.set(container, lastInsertedChild->nodeIndex() + 1, lastInsertedChild.get()); - } - } + return { }; } -String Range::toString(ExceptionCode& ec) const +String Range::toString() const { - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return String(); - } - StringBuilder builder; Node* pastLast = pastLastNode(); - for (Node* n = firstNode(); n != pastLast; n = NodeTraversal::next(n)) { - if (n->nodeType() == Node::TEXT_NODE || n->nodeType() == Node::CDATA_SECTION_NODE) { - const String& data = static_cast<CharacterData*>(n)->data(); - int length = data.length(); - int start = (n == m_start.container()) ? std::min(std::max(0, m_start.offset()), length) : 0; - int end = (n == m_end.container()) ? std::min(std::max(start, m_end.offset()), length) : length; + for (Node* node = firstNode(); node != pastLast; node = NodeTraversal::next(*node)) { + auto type = node->nodeType(); + if (type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE) { + auto& data = downcast<CharacterData>(*node).data(); + unsigned length = data.length(); + unsigned start = node == &startContainer() ? std::min(m_start.offset(), length) : 0U; + unsigned end = node == &endContainer() ? std::min(std::max(start, m_end.offset()), length) : length; builder.append(data, start, end - start); } } @@ -1102,536 +960,229 @@ String Range::toHTML() const String Range::text() const { - if (!m_start.container()) - return String(); - // We need to update layout, since plainText uses line boxes in the render tree. // FIXME: As with innerText, we'd like this to work even if there are no render objects. - m_start.container()->document().updateLayout(); + startContainer().document().updateLayout(); return plainText(this); } -PassRefPtr<DocumentFragment> Range::createContextualFragment(const String& markup, ExceptionCode& ec) +// https://w3c.github.io/DOM-Parsing/#widl-Range-createContextualFragment-DocumentFragment-DOMString-fragment +ExceptionOr<Ref<DocumentFragment>> Range::createContextualFragment(const String& markup) { - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return 0; - } - - Node* element = m_start.container()->isElementNode() ? m_start.container() : m_start.container()->parentNode(); - if (!element || !element->isHTMLElement()) { - ec = NOT_SUPPORTED_ERR; - return 0; - } - - RefPtr<DocumentFragment> fragment = WebCore::createContextualFragment(markup, toHTMLElement(element), AllowScriptingContentAndDoNotMarkAlreadyStarted, ec); - if (!fragment) - return 0; - - return fragment.release(); + Node& node = startContainer(); + RefPtr<Element> element; + if (is<Document>(node) || is<DocumentFragment>(node)) + element = nullptr; + else if (is<Element>(node)) + element = &downcast<Element>(node); + else + element = node.parentElement(); + if (!element || (is<HTMLDocument>(element->document()) && is<HTMLHtmlElement>(*element))) + element = HTMLBodyElement::create(node.document()); + return WebCore::createContextualFragment(*element, markup, AllowScriptingContentAndDoNotMarkAlreadyStarted); } - -void Range::detach(ExceptionCode& ec) +void Range::detach() { - // Check first to see if we've already detached: - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return; - } - - m_ownerDocument->detachRange(this); - - m_start.clear(); - m_end.clear(); + // This is now a no-op as per the DOM specification. } -Node* Range::checkNodeWOffset(Node* n, int offset, ExceptionCode& ec) const +ExceptionOr<Node*> Range::checkNodeWOffset(Node& node, unsigned offset) const { - switch (n->nodeType()) { + switch (node.nodeType()) { case Node::DOCUMENT_TYPE_NODE: - case Node::ENTITY_NODE: - case Node::NOTATION_NODE: - ec = RangeException::INVALID_NODE_TYPE_ERR; - return 0; + return Exception { INVALID_NODE_TYPE_ERR }; case Node::CDATA_SECTION_NODE: case Node::COMMENT_NODE: case Node::TEXT_NODE: - if (static_cast<unsigned>(offset) > toCharacterData(n)->length()) - ec = INDEX_SIZE_ERR; - return 0; case Node::PROCESSING_INSTRUCTION_NODE: - if (static_cast<unsigned>(offset) > toProcessingInstruction(n)->data().length()) - ec = INDEX_SIZE_ERR; - return 0; + if (offset > downcast<CharacterData>(node).length()) + return Exception { INDEX_SIZE_ERR }; + return nullptr; case Node::ATTRIBUTE_NODE: case Node::DOCUMENT_FRAGMENT_NODE: case Node::DOCUMENT_NODE: - case Node::ELEMENT_NODE: - case Node::ENTITY_REFERENCE_NODE: - case Node::XPATH_NAMESPACE_NODE: { + case Node::ELEMENT_NODE: { if (!offset) - return 0; - Node* childBefore = n->childNode(offset - 1); + return nullptr; + Node* childBefore = node.traverseToChildAt(offset - 1); if (!childBefore) - ec = INDEX_SIZE_ERR; + return Exception { INDEX_SIZE_ERR }; return childBefore; } } ASSERT_NOT_REACHED(); - return 0; + return Exception { INVALID_NODE_TYPE_ERR }; } -void Range::checkNodeBA(Node* n, ExceptionCode& ec) const +Ref<Range> Range::cloneRange() const { - // INVALID_NODE_TYPE_ERR: Raised if the root container of refNode is not an - // Attr, Document, DocumentFragment or ShadowRoot node, or part of a SVG shadow DOM tree, - // or if refNode is a Document, DocumentFragment, ShadowRoot, Attr, Entity, or Notation node. - - switch (n->nodeType()) { - case Node::ATTRIBUTE_NODE: - case Node::DOCUMENT_FRAGMENT_NODE: - case Node::DOCUMENT_NODE: - case Node::ENTITY_NODE: - case Node::NOTATION_NODE: - ec = RangeException::INVALID_NODE_TYPE_ERR; - return; - case Node::CDATA_SECTION_NODE: - case Node::COMMENT_NODE: - case Node::DOCUMENT_TYPE_NODE: - case Node::ELEMENT_NODE: - case Node::ENTITY_REFERENCE_NODE: - case Node::PROCESSING_INSTRUCTION_NODE: - case Node::TEXT_NODE: - case Node::XPATH_NAMESPACE_NODE: - break; - } - - Node* root = n; - while (ContainerNode* parent = root->parentNode()) - root = parent; - - switch (root->nodeType()) { - case Node::ATTRIBUTE_NODE: - case Node::DOCUMENT_NODE: - case Node::DOCUMENT_FRAGMENT_NODE: - break; - case Node::CDATA_SECTION_NODE: - case Node::COMMENT_NODE: - case Node::DOCUMENT_TYPE_NODE: - case Node::ELEMENT_NODE: - case Node::ENTITY_NODE: - case Node::ENTITY_REFERENCE_NODE: - case Node::NOTATION_NODE: - case Node::PROCESSING_INSTRUCTION_NODE: - case Node::TEXT_NODE: - case Node::XPATH_NAMESPACE_NODE: - ec = RangeException::INVALID_NODE_TYPE_ERR; - return; - } + return Range::create(ownerDocument(), &startContainer(), m_start.offset(), &endContainer(), m_end.offset()); } -PassRefPtr<Range> Range::cloneRange(ExceptionCode& ec) const +ExceptionOr<void> Range::setStartAfter(Node& refNode) { - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return 0; - } - - return Range::create(ownerDocument(), m_start.container(), m_start.offset(), m_end.container(), m_end.offset()); + if (!refNode.parentNode()) + return Exception { INVALID_NODE_TYPE_ERR }; + return setStart(*refNode.parentNode(), refNode.computeNodeIndex() + 1); } -void Range::setStartAfter(Node* refNode, ExceptionCode& ec) +ExceptionOr<void> Range::setEndBefore(Node& refNode) { - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return; - } - - if (!refNode) { - ec = NOT_FOUND_ERR; - return; - } - - ec = 0; - checkNodeBA(refNode, ec); - if (ec) - return; - - setStart(refNode->parentNode(), refNode->nodeIndex() + 1, ec); + if (!refNode.parentNode()) + return Exception { INVALID_NODE_TYPE_ERR }; + return setEnd(*refNode.parentNode(), refNode.computeNodeIndex()); } -void Range::setEndBefore(Node* refNode, ExceptionCode& ec) +ExceptionOr<void> Range::setEndAfter(Node& refNode) { - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return; - } - - if (!refNode) { - ec = NOT_FOUND_ERR; - return; - } - - ec = 0; - checkNodeBA(refNode, ec); - if (ec) - return; - - setEnd(refNode->parentNode(), refNode->nodeIndex(), ec); + if (!refNode.parentNode()) + return Exception { INVALID_NODE_TYPE_ERR }; + return setEnd(*refNode.parentNode(), refNode.computeNodeIndex() + 1); } -void Range::setEndAfter(Node* refNode, ExceptionCode& ec) +ExceptionOr<void> Range::selectNode(Node& refNode) { - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return; - } - - if (!refNode) { - ec = NOT_FOUND_ERR; - return; - } - - ec = 0; - checkNodeBA(refNode, ec); - if (ec) - return; - - setEnd(refNode->parentNode(), refNode->nodeIndex() + 1, ec); -} - -void Range::selectNode(Node* refNode, ExceptionCode& ec) -{ - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return; - } - - if (!refNode) { - ec = NOT_FOUND_ERR; - return; - } - - // INVALID_NODE_TYPE_ERR: Raised if an ancestor of refNode is an Entity, Notation or - // DocumentType node or if refNode is a Document, DocumentFragment, ShadowRoot, Attr, Entity, or Notation - // node. - for (ContainerNode* anc = refNode->parentNode(); anc; anc = anc->parentNode()) { - switch (anc->nodeType()) { - case Node::ATTRIBUTE_NODE: - case Node::CDATA_SECTION_NODE: - case Node::COMMENT_NODE: - case Node::DOCUMENT_FRAGMENT_NODE: - case Node::DOCUMENT_NODE: - case Node::ELEMENT_NODE: - case Node::ENTITY_REFERENCE_NODE: - case Node::PROCESSING_INSTRUCTION_NODE: - case Node::TEXT_NODE: - case Node::XPATH_NAMESPACE_NODE: - break; - case Node::DOCUMENT_TYPE_NODE: - case Node::ENTITY_NODE: - case Node::NOTATION_NODE: - ec = RangeException::INVALID_NODE_TYPE_ERR; - return; - } - } - - switch (refNode->nodeType()) { - case Node::CDATA_SECTION_NODE: - case Node::COMMENT_NODE: - case Node::DOCUMENT_TYPE_NODE: - case Node::ELEMENT_NODE: - case Node::ENTITY_REFERENCE_NODE: - case Node::PROCESSING_INSTRUCTION_NODE: - case Node::TEXT_NODE: - case Node::XPATH_NAMESPACE_NODE: - break; - case Node::ATTRIBUTE_NODE: - case Node::DOCUMENT_FRAGMENT_NODE: - case Node::DOCUMENT_NODE: - case Node::ENTITY_NODE: - case Node::NOTATION_NODE: - ec = RangeException::INVALID_NODE_TYPE_ERR; - return; - } + if (!refNode.parentNode()) + return Exception { INVALID_NODE_TYPE_ERR }; - if (&ownerDocument() != &refNode->document()) - setDocument(refNode->document()); + if (&ownerDocument() != &refNode.document()) + setDocument(refNode.document()); - ec = 0; - setStartBefore(refNode, ec); - if (ec) - return; - setEndAfter(refNode, ec); + unsigned index = refNode.computeNodeIndex(); + auto result = setStart(*refNode.parentNode(), index); + if (result.hasException()) + return result.releaseException(); + return setEnd(*refNode.parentNode(), index + 1); } -void Range::selectNodeContents(Node* refNode, ExceptionCode& ec) +ExceptionOr<void> Range::selectNodeContents(Node& refNode) { - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return; - } - - if (!refNode) { - ec = NOT_FOUND_ERR; - return; - } - - // INVALID_NODE_TYPE_ERR: Raised if refNode or an ancestor of refNode is an Entity, Notation - // or DocumentType node. - for (Node* n = refNode; n; n = n->parentNode()) { - switch (n->nodeType()) { - case Node::ATTRIBUTE_NODE: - case Node::CDATA_SECTION_NODE: - case Node::COMMENT_NODE: - case Node::DOCUMENT_FRAGMENT_NODE: - case Node::DOCUMENT_NODE: - case Node::ELEMENT_NODE: - case Node::ENTITY_REFERENCE_NODE: - case Node::PROCESSING_INSTRUCTION_NODE: - case Node::TEXT_NODE: - case Node::XPATH_NAMESPACE_NODE: - break; - case Node::DOCUMENT_TYPE_NODE: - case Node::ENTITY_NODE: - case Node::NOTATION_NODE: - ec = RangeException::INVALID_NODE_TYPE_ERR; - return; - } - } + if (refNode.isDocumentTypeNode()) + return Exception { INVALID_NODE_TYPE_ERR }; - if (&ownerDocument() != &refNode->document()) - setDocument(refNode->document()); + if (&ownerDocument() != &refNode.document()) + setDocument(refNode.document()); m_start.setToStartOfNode(refNode); m_end.setToEndOfNode(refNode); + + return { }; } -void Range::surroundContents(PassRefPtr<Node> passNewParent, ExceptionCode& ec) +// https://dom.spec.whatwg.org/#dom-range-surroundcontents +ExceptionOr<void> Range::surroundContents(Node& newParent) { - RefPtr<Node> newParent = passNewParent; - - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return; - } + Ref<Node> protectedNewParent(newParent); - if (!newParent) { - ec = NOT_FOUND_ERR; - return; - } + // Step 1: If a non-Text node is partially contained in the context object, then throw an InvalidStateError. + Node* startNonTextContainer = &startContainer(); + if (startNonTextContainer->nodeType() == Node::TEXT_NODE) + startNonTextContainer = startNonTextContainer->parentNode(); + Node* endNonTextContainer = &endContainer(); + if (endNonTextContainer->nodeType() == Node::TEXT_NODE) + endNonTextContainer = endNonTextContainer->parentNode(); + if (startNonTextContainer != endNonTextContainer) + return Exception { INVALID_STATE_ERR }; - // INVALID_NODE_TYPE_ERR: Raised if node is an Attr, Entity, DocumentType, Notation, - // Document, or DocumentFragment node. - switch (newParent->nodeType()) { + // Step 2: If newParent is a Document, DocumentType, or DocumentFragment node, then throw an InvalidNodeTypeError. + switch (newParent.nodeType()) { case Node::ATTRIBUTE_NODE: case Node::DOCUMENT_FRAGMENT_NODE: case Node::DOCUMENT_NODE: case Node::DOCUMENT_TYPE_NODE: - case Node::ENTITY_NODE: - case Node::NOTATION_NODE: - ec = RangeException::INVALID_NODE_TYPE_ERR; - return; + return Exception { INVALID_NODE_TYPE_ERR }; case Node::CDATA_SECTION_NODE: case Node::COMMENT_NODE: case Node::ELEMENT_NODE: - case Node::ENTITY_REFERENCE_NODE: case Node::PROCESSING_INSTRUCTION_NODE: case Node::TEXT_NODE: - case Node::XPATH_NAMESPACE_NODE: break; } - // NO_MODIFICATION_ALLOWED_ERR: Raised if an ancestor container of either boundary-point of - // the Range is read-only. - if (containedByReadOnly()) { - ec = NO_MODIFICATION_ALLOWED_ERR; - return; - } - - // Raise a HIERARCHY_REQUEST_ERR if m_start.container() doesn't accept children like newParent. - Node* parentOfNewParent = m_start.container(); - - // If m_start.container() is a character data node, it will be split and it will be its parent that will - // need to accept newParent (or in the case of a comment, it logically "would" be inserted into the parent, - // although this will fail below for another reason). - if (parentOfNewParent->isCharacterDataNode()) - parentOfNewParent = parentOfNewParent->parentNode(); - if (!parentOfNewParent || !parentOfNewParent->childTypeAllowed(newParent->nodeType())) { - ec = HIERARCHY_REQUEST_ERR; - return; - } - - if (newParent->contains(m_start.container())) { - ec = HIERARCHY_REQUEST_ERR; - return; - } - - // FIXME: Do we need a check if the node would end up with a child node of a type not - // allowed by the type of node? - - // BAD_BOUNDARYPOINTS_ERR: Raised if the Range partially selects a non-Text node. - Node* startNonTextContainer = m_start.container(); - if (startNonTextContainer->nodeType() == Node::TEXT_NODE) - startNonTextContainer = startNonTextContainer->parentNode(); - Node* endNonTextContainer = m_end.container(); - if (endNonTextContainer->nodeType() == Node::TEXT_NODE) - endNonTextContainer = endNonTextContainer->parentNode(); - if (startNonTextContainer != endNonTextContainer) { - ec = RangeException::BAD_BOUNDARYPOINTS_ERR; - return; - } - - ec = 0; - while (Node* n = newParent->firstChild()) { - toContainerNode(newParent.get())->removeChild(n, ec); - if (ec) - return; - } - RefPtr<DocumentFragment> fragment = extractContents(ec); - if (ec) - return; - insertNode(newParent, ec); - if (ec) - return; - newParent->appendChild(fragment.release(), ec); - if (ec) - return; - selectNode(newParent.get(), ec); -} - -void Range::setStartBefore(Node* refNode, ExceptionCode& ec) -{ - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return; - } - - if (!refNode) { - ec = NOT_FOUND_ERR; - return; - } + // Step 3: Let fragment be the result of extracting context object. + auto fragment = extractContents(); + if (fragment.hasException()) + return fragment.releaseException(); - ec = 0; - checkNodeBA(refNode, ec); - if (ec) - return; + // Step 4: If newParent has children, replace all with null within newParent. + if (newParent.hasChildNodes()) + downcast<ContainerNode>(newParent).replaceAllChildren(nullptr); - setStart(refNode->parentNode(), refNode->nodeIndex(), ec); -} + // Step 5: Insert newParent into context object. + auto insertResult = insertNode(newParent); + if (insertResult.hasException()) + return insertResult.releaseException(); -void Range::checkDeleteExtract(ExceptionCode& ec) -{ - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return; - } - - ec = 0; - if (!commonAncestorContainer(ec) || ec) - return; - - Node* pastLast = pastLastNode(); - for (Node* n = firstNode(); n != pastLast; n = NodeTraversal::next(n)) { - if (n->isReadOnlyNode()) { - ec = NO_MODIFICATION_ALLOWED_ERR; - return; - } - if (n->nodeType() == Node::DOCUMENT_TYPE_NODE) { - ec = HIERARCHY_REQUEST_ERR; - return; - } - } + // Step 6: Append fragment to newParent. + auto appendResult = newParent.appendChild(fragment.releaseReturnValue()); + if (appendResult.hasException()) + return appendResult.releaseException(); - if (containedByReadOnly()) { - ec = NO_MODIFICATION_ALLOWED_ERR; - return; - } + // Step 7: Select newParent within context object. + return selectNode(newParent); } -bool Range::containedByReadOnly() const +ExceptionOr<void> Range::setStartBefore(Node& refNode) { - for (Node* n = m_start.container(); n; n = n->parentNode()) { - if (n->isReadOnlyNode()) - return true; - } - for (Node* n = m_end.container(); n; n = n->parentNode()) { - if (n->isReadOnlyNode()) - return true; - } - return false; + if (!refNode.parentNode()) + return Exception { INVALID_NODE_TYPE_ERR }; + return setStart(*refNode.parentNode(), refNode.computeNodeIndex()); } Node* Range::firstNode() const { - if (!m_start.container()) - return 0; - if (m_start.container()->offsetInCharacters()) - return m_start.container(); - if (isRendererReplacedElement(m_start.container()->renderer())) - return m_start.container(); - if (Node* child = m_start.container()->childNode(m_start.offset())) + if (startContainer().offsetInCharacters()) + return &startContainer(); + if (Node* child = startContainer().traverseToChildAt(m_start.offset())) return child; if (!m_start.offset()) - return m_start.container(); - return NodeTraversal::nextSkippingChildren(m_start.container()); + return &startContainer(); + return NodeTraversal::nextSkippingChildren(startContainer()); } ShadowRoot* Range::shadowRoot() const { - return startContainer() ? startContainer()->containingShadowRoot() : 0; + return startContainer().containingShadowRoot(); } Node* Range::pastLastNode() const { - if (!m_start.container() || !m_end.container()) - return 0; - if (m_end.container()->offsetInCharacters()) - return NodeTraversal::nextSkippingChildren(m_end.container()); - if (Node* child = m_end.container()->childNode(m_end.offset())) + if (endContainer().offsetInCharacters()) + return NodeTraversal::nextSkippingChildren(endContainer()); + if (Node* child = endContainer().traverseToChildAt(m_end.offset())) return child; - return NodeTraversal::nextSkippingChildren(m_end.container()); + return NodeTraversal::nextSkippingChildren(endContainer()); } -IntRect Range::boundingBox() const +IntRect Range::absoluteBoundingBox() const { IntRect result; Vector<IntRect> rects; - textRects(rects); - const size_t n = rects.size(); - for (size_t i = 0; i < n; ++i) - result.unite(rects[i]); + absoluteTextRects(rects); + for (auto& rect : rects) + result.unite(rect); return result; } -void Range::textRects(Vector<IntRect>& rects, bool useSelectionHeight, RangeInFixedPosition* inFixed) const +void Range::absoluteTextRects(Vector<IntRect>& rects, bool useSelectionHeight, RangeInFixedPosition* inFixed) const { - Node* startContainer = m_start.container(); - Node* endContainer = m_end.container(); - - if (!startContainer || !endContainer) { - if (inFixed) - *inFixed = NotFixedPosition; - return; - } - bool allFixed = true; bool someFixed = false; Node* stopNode = pastLastNode(); - for (Node* node = firstNode(); node != stopNode; node = NodeTraversal::next(node)) { - RenderObject* r = node->renderer(); - if (!r) + for (Node* node = firstNode(); node != stopNode; node = NodeTraversal::next(*node)) { + RenderObject* renderer = node->renderer(); + if (!renderer) continue; bool isFixed = false; - if (r->isBR()) - r->absoluteRects(rects, flooredLayoutPoint(r->localToAbsolute())); - else if (r->isText()) { - int startOffset = node == startContainer ? m_start.offset() : 0; - int endOffset = node == endContainer ? m_end.offset() : std::numeric_limits<int>::max(); - rects.appendVector(toRenderText(r)->absoluteRectsForRange(startOffset, endOffset, useSelectionHeight, &isFixed)); + if (renderer->isBR()) + renderer->absoluteRects(rects, flooredLayoutPoint(renderer->localToAbsolute())); + else if (is<RenderText>(*renderer)) { + unsigned startOffset = node == &startContainer() ? m_start.offset() : 0; + unsigned endOffset = node == &endContainer() ? m_end.offset() : std::numeric_limits<unsigned>::max(); + rects.appendVector(downcast<RenderText>(*renderer).absoluteRectsForRange(startOffset, endOffset, useSelectionHeight, &isFixed)); } else continue; allFixed &= isFixed; @@ -1642,32 +1193,23 @@ void Range::textRects(Vector<IntRect>& rects, bool useSelectionHeight, RangeInFi *inFixed = allFixed ? EntirelyFixedPosition : (someFixed ? PartiallyFixedPosition : NotFixedPosition); } -void Range::textQuads(Vector<FloatQuad>& quads, bool useSelectionHeight, RangeInFixedPosition* inFixed) const +void Range::absoluteTextQuads(Vector<FloatQuad>& quads, bool useSelectionHeight, RangeInFixedPosition* inFixed) const { - Node* startContainer = m_start.container(); - Node* endContainer = m_end.container(); - - if (!startContainer || !endContainer) { - if (inFixed) - *inFixed = NotFixedPosition; - return; - } - bool allFixed = true; bool someFixed = false; Node* stopNode = pastLastNode(); - for (Node* node = firstNode(); node != stopNode; node = NodeTraversal::next(node)) { - RenderObject* r = node->renderer(); - if (!r) + for (Node* node = firstNode(); node != stopNode; node = NodeTraversal::next(*node)) { + RenderObject* renderer = node->renderer(); + if (!renderer) continue; bool isFixed = false; - if (r->isBR()) - r->absoluteQuads(quads, &isFixed); - else if (r->isText()) { - int startOffset = node == startContainer ? m_start.offset() : 0; - int endOffset = node == endContainer ? m_end.offset() : std::numeric_limits<int>::max(); - quads.appendVector(toRenderText(r)->absoluteQuadsForRange(startOffset, endOffset, useSelectionHeight, &isFixed)); + if (renderer->isBR()) + renderer->absoluteQuads(quads, &isFixed); + else if (is<RenderText>(*renderer)) { + unsigned startOffset = node == &startContainer() ? m_start.offset() : 0; + unsigned endOffset = node == &endContainer() ? m_end.offset() : std::numeric_limits<unsigned>::max(); + quads.appendVector(downcast<RenderText>(*renderer).absoluteQuadsForRange(startOffset, endOffset, useSelectionHeight, &isFixed)); } else continue; allFixed &= isFixed; @@ -1698,28 +1240,6 @@ static bool intervalsSufficientlyOverlap(int startA, int endA, int startB, int e return minEnd - maxStart >= sufficientOverlap * std::min(lengthA, lengthB); } -#ifndef NDEBUG -static void printRects(Vector<SelectionRect>& rects) -{ - size_t numberOfRects = rects.size(); - for (size_t i = 0; i < numberOfRects; ++i) { - fprintf(stderr, "%zu\t[%d, %d] - [%d, %d]\t%c %s\tis first: %s\tis last:%s\tcontains start: %s\tcontains end: %s\tline: %d\truby: %s\tcolumn: %d\n", - i, - rects[i].rect().x(), rects[i].rect().y(), rects[i].rect().width(), rects[i].rect().height(), - rects[i].isHorizontal() ? 'H' : 'V', - rects[i].direction() == LTR ? "LTR" : "RTL", - rects[i].isFirstOnLine() ? "yes" : "no", - rects[i].isLastOnLine() ? "yes" : "no", - rects[i].containsStart() ? "yes" : "no", - rects[i].containsEnd() ? "yes" : "no", - rects[i].lineNumber(), - rects[i].isRubyText() ? "yes": "no", - rects[i].columnNumber()); - } - fprintf(stderr, "--------------------------------------\n"); -} -#endif - static inline void adjustLineHeightOfSelectionRects(Vector<SelectionRect>& rects, size_t numberOfRects, int lineNumber, int lineTop, int lineHeight) { ASSERT(rects.size() >= numberOfRects); @@ -1735,7 +1255,7 @@ static inline void adjustLineHeightOfSelectionRects(Vector<SelectionRect>& rects static SelectionRect coalesceSelectionRects(const SelectionRect& original, const SelectionRect& previous) { - SelectionRect result(unionRect(previous.rect(), original.rect()), original.isHorizontal(), original.columnNumber()); + SelectionRect result(unionRect(previous.rect(), original.rect()), original.isHorizontal(), original.pageNumber()); result.setDirection(original.containsStart() || original.containsEnd() ? original.direction() : previous.direction()); result.setContainsStart(previous.containsStart() || original.containsStart()); result.setContainsEnd(previous.containsEnd() || original.containsEnd()); @@ -1746,26 +1266,23 @@ static SelectionRect coalesceSelectionRects(const SelectionRect& original, const // This function is similar in spirit to addLineBoxRects, but annotates the returned rectangles // with additional state which helps iOS draw selections in its unique way. -void Range::collectSelectionRects(Vector<SelectionRect>& rects) +int Range::collectSelectionRectsWithoutUnionInteriorLines(Vector<SelectionRect>& rects) { - if (!m_start.container() || !m_end.container()) - return; - - Node* startContainer = m_start.container(); - Node* endContainer = m_end.container(); + auto& startContainer = this->startContainer(); + auto& endContainer = this->endContainer(); int startOffset = m_start.offset(); int endOffset = m_end.offset(); Vector<SelectionRect> newRects; Node* stopNode = pastLastNode(); - bool hasFlippedWritingMode = startContainer->renderer() && startContainer->renderer()->style().isFlippedBlocksWritingMode(); + bool hasFlippedWritingMode = startContainer.renderer() && startContainer.renderer()->style().isFlippedBlocksWritingMode(); bool containsDifferentWritingModes = false; - for (Node* node = firstNode(); node && node != stopNode; node = NodeTraversal::next(node)) { + for (Node* node = firstNode(); node && node != stopNode; node = NodeTraversal::next(*node)) { RenderObject* renderer = node->renderer(); // Only ask leaf render objects for their line box rects. if (renderer && !renderer->firstChildSlow() && renderer->style().userSelect() != SELECT_NONE) { - bool isStartNode = renderer->node() == startContainer; - bool isEndNode = renderer->node() == endContainer; + bool isStartNode = renderer->node() == &startContainer; + bool isEndNode = renderer->node() == &endContainer; if (hasFlippedWritingMode != renderer->style().isFlippedBlocksWritingMode()) containsDifferentWritingModes = true; // FIXME: Sending 0 for the startOffset is a weird way of telling the renderer that the selection @@ -1777,28 +1294,22 @@ void Range::collectSelectionRects(Vector<SelectionRect>& rects) int beginSelectionOffset = isStartNode ? startOffset : 0; int endSelectionOffset = isEndNode ? endOffset : std::numeric_limits<int>::max(); renderer->collectSelectionRects(newRects, beginSelectionOffset, endSelectionOffset); - size_t numberOfNewRects = newRects.size(); - for (size_t i = 0; i < numberOfNewRects; ++i) { - SelectionRect& selectionRect = newRects[i]; + for (auto& selectionRect : newRects) { if (selectionRect.containsStart() && !isStartNode) selectionRect.setContainsStart(false); if (selectionRect.containsEnd() && !isEndNode) selectionRect.setContainsEnd(false); if (selectionRect.logicalWidth() || selectionRect.logicalHeight()) - rects.append(newRects[i]); + rects.append(selectionRect); } newRects.shrink(0); } } -#ifndef NDEBUG - printRects(rects); -#endif - // The range could span over nodes with different writing modes. // If this is the case, we use the writing mode of the common ancestor. if (containsDifferentWritingModes) { - if (Node* ancestor = commonAncestorContainer(startContainer, endContainer)) + if (Node* ancestor = commonAncestorContainer(&startContainer, &endContainer)) hasFlippedWritingMode = ancestor->renderer()->style().isFlippedBlocksWritingMode(); } @@ -1811,10 +1322,10 @@ void Range::collectSelectionRects(Vector<SelectionRect>& rects) // Only set the line break bit if the end of the range actually // extends all the way to include the <br>. VisiblePosition helps to // figure this out. - VisiblePosition endPosition(createLegacyEditingPosition(endContainer, endOffset), VP_DEFAULT_AFFINITY); + VisiblePosition endPosition(createLegacyEditingPosition(&endContainer, endOffset), VP_DEFAULT_AFFINITY); VisiblePosition brPosition(createLegacyEditingPosition(stopNode, 0), VP_DEFAULT_AFFINITY); if (endPosition == brPosition) - rects.last().setIsLineBreak(true); + rects.last().setIsLineBreak(true); } int lineTop = std::numeric_limits<int>::max(); @@ -1851,7 +1362,7 @@ void Range::collectSelectionRects(Vector<SelectionRect>& rects) lineBottom = currentRectBottom; } else { lastLineBottom = lineBottom; - if (currentRectTop <= lastLineBottom && i && rects[i].columnNumber() == rects[i - 1].columnNumber()) { + if (currentRectTop <= lastLineBottom && i && rects[i].pageNumber() == rects[i - 1].pageNumber()) { lastLineTop = lineTop; lineBottom = lastLineTop; } else { @@ -1911,7 +1422,15 @@ void Range::collectSelectionRects(Vector<SelectionRect>& rects) } else if (selectionRect.direction() == LTR && selectionRect.isLastOnLine()) selectionRect.setLogicalWidth(selectionRect.maxX() - selectionRect.logicalLeft()); } + + return maxLineNumber; +} +void Range::collectSelectionRects(Vector<SelectionRect>& rects) +{ + int maxLineNumber = collectSelectionRectsWithoutUnionInteriorLines(rects); + const size_t numberOfRects = rects.size(); + // Union all the rectangles on interior lines (i.e. not first or last). // On first and last lines, just avoid having overlaps by merging intersecting rectangles. Vector<SelectionRect> unionedRects; @@ -1942,13 +1461,13 @@ void Range::collectSelectionRects(Vector<SelectionRect>& rects) // For iBooks, the interior lines may cross multiple horizontal pages. interiorUnionRect.unite(currentRect.rect()); } else { - unionedRects.append(SelectionRect(interiorUnionRect, currentRect.isHorizontal(), currentRect.columnNumber())); + unionedRects.append(SelectionRect(interiorUnionRect, currentRect.isHorizontal(), currentRect.pageNumber())); interiorUnionRect = currentRect.rect(); } } else { // Processing last line. if (!interiorUnionRect.isEmpty()) { - unionedRects.append(SelectionRect(interiorUnionRect, currentRect.isHorizontal(), currentRect.columnNumber())); + unionedRects.append(SelectionRect(interiorUnionRect, currentRect.isHorizontal(), currentRect.pageNumber())); interiorUnionRect = IntRect(); } @@ -1968,33 +1487,49 @@ void Range::collectSelectionRects(Vector<SelectionRect>& rects) } #endif -#ifndef NDEBUG +#if ENABLE(TREE_DEBUGGING) void Range::formatForDebugger(char* buffer, unsigned length) const { StringBuilder result; - String s; - - if (!m_start.container() || !m_end.container()) - result.appendLiteral("<empty>"); - else { - const int FormatBufferSize = 1024; - char s[FormatBufferSize]; - result.appendLiteral("from offset "); - result.appendNumber(m_start.offset()); - result.appendLiteral(" of "); - m_start.container()->formatForDebugger(s, FormatBufferSize); - result.append(s); - result.appendLiteral(" to offset "); - result.appendNumber(m_end.offset()); - result.appendLiteral(" of "); - m_end.container()->formatForDebugger(s, FormatBufferSize); - result.append(s); - } + + const int FormatBufferSize = 1024; + char s[FormatBufferSize]; + result.appendLiteral("from offset "); + result.appendNumber(m_start.offset()); + result.appendLiteral(" of "); + startContainer().formatForDebugger(s, FormatBufferSize); + result.append(s); + result.appendLiteral(" to offset "); + result.appendNumber(m_end.offset()); + result.appendLiteral(" of "); + endContainer().formatForDebugger(s, FormatBufferSize); + result.append(s); strncpy(buffer, result.toString().utf8().data(), length - 1); } #endif +bool Range::contains(const Range& other) const +{ + if (commonAncestorContainer()->ownerDocument() != other.commonAncestorContainer()->ownerDocument()) + return false; + + auto startToStart = compareBoundaryPoints(Range::START_TO_START, other); + if (startToStart.hasException() || startToStart.releaseReturnValue() > 0) + return false; + + auto endToEnd = compareBoundaryPoints(Range::END_TO_END, other); + return !endToEnd.hasException() && endToEnd.releaseReturnValue() >= 0; +} + +bool Range::contains(const VisiblePosition& position) const +{ + RefPtr<Range> positionRange = makeRange(position, position); + if (!positionRange) + return false; + return contains(*positionRange); +} + bool areRangesEqual(const Range* a, const Range* b) { if (a == b) @@ -2004,30 +1539,40 @@ bool areRangesEqual(const Range* a, const Range* b) return a->startPosition() == b->startPosition() && a->endPosition() == b->endPosition(); } -PassRefPtr<Range> rangeOfContents(Node& node) +bool rangesOverlap(const Range* a, const Range* b) { - RefPtr<Range> range = Range::create(node.document()); - int exception = 0; - range->selectNodeContents(&node, exception); - return range.release(); -} + if (!a || !b) + return false; -int Range::maxStartOffset() const -{ - if (!m_start.container()) - return 0; - if (!m_start.container()->offsetInCharacters()) - return m_start.container()->childNodeCount(); - return m_start.container()->maxCharacterOffset(); + if (a == b) + return true; + + if (a->commonAncestorContainer()->ownerDocument() != b->commonAncestorContainer()->ownerDocument()) + return false; + + short startToStart = a->compareBoundaryPoints(Range::START_TO_START, *b).releaseReturnValue(); + short endToEnd = a->compareBoundaryPoints(Range::END_TO_END, *b).releaseReturnValue(); + + // First range contains the second range. + if (startToStart <= 0 && endToEnd >= 0) + return true; + + // End of first range is inside second range. + if (a->compareBoundaryPoints(Range::START_TO_END, *b).releaseReturnValue() >= 0 && endToEnd <= 0) + return true; + + // Start of first range is inside second range. + if (startToStart >= 0 && a->compareBoundaryPoints(Range::END_TO_START, *b).releaseReturnValue() <= 0) + return true; + + return false; } -int Range::maxEndOffset() const +Ref<Range> rangeOfContents(Node& node) { - if (!m_end.container()) - return 0; - if (!m_end.container()->offsetInCharacters()) - return m_end.container()->childNodeCount(); - return m_end.container()->maxCharacterOffset(); + auto range = Range::create(node.document()); + range->selectNodeContents(node); + return range; } static inline void boundaryNodeChildrenChanged(RangeBoundaryPoint& boundary, ContainerNode& container) @@ -2050,13 +1595,13 @@ static inline void boundaryNodeChildrenWillBeRemoved(RangeBoundaryPoint& boundar { for (Node* nodeToBeRemoved = container.firstChild(); nodeToBeRemoved; nodeToBeRemoved = nodeToBeRemoved->nextSibling()) { if (boundary.childBefore() == nodeToBeRemoved) { - boundary.setToStartOfNode(&container); + boundary.setToStartOfNode(container); return; } for (Node* n = boundary.container(); n; n = n->parentNode()) { if (n == nodeToBeRemoved) { - boundary.setToStartOfNode(&container); + boundary.setToStartOfNode(container); return; } } @@ -2070,27 +1615,26 @@ void Range::nodeChildrenWillBeRemoved(ContainerNode& container) boundaryNodeChildrenWillBeRemoved(m_end, container); } -static inline void boundaryNodeWillBeRemoved(RangeBoundaryPoint& boundary, Node* nodeToBeRemoved) +static inline void boundaryNodeWillBeRemoved(RangeBoundaryPoint& boundary, Node& nodeToBeRemoved) { - if (boundary.childBefore() == nodeToBeRemoved) { + if (boundary.childBefore() == &nodeToBeRemoved) { boundary.childBeforeWillBeRemoved(); return; } for (Node* n = boundary.container(); n; n = n->parentNode()) { - if (n == nodeToBeRemoved) { + if (n == &nodeToBeRemoved) { boundary.setToBeforeChild(nodeToBeRemoved); return; } } } -void Range::nodeWillBeRemoved(Node* node) +void Range::nodeWillBeRemoved(Node& node) { - ASSERT(node); - ASSERT(&node->document() == &ownerDocument()); - ASSERT(node != &ownerDocument()); - ASSERT(node->parentNode()); + ASSERT(&node.document() == &ownerDocument()); + ASSERT(&node != &ownerDocument()); + ASSERT(node.parentNode()); boundaryNodeWillBeRemoved(m_start, node); boundaryNodeWillBeRemoved(m_end, node); } @@ -2137,9 +1681,9 @@ void Range::textRemoved(Node* text, unsigned offset, unsigned length) static inline void boundaryTextNodesMerged(RangeBoundaryPoint& boundary, NodeWithIndex& oldNode, unsigned offset) { if (boundary.container() == oldNode.node()) - boundary.set(oldNode.node()->previousSibling(), boundary.offset() + offset, 0); - else if (boundary.container() == oldNode.node()->parentNode() && boundary.offset() == oldNode.index()) - boundary.set(oldNode.node()->previousSibling(), offset, 0); + boundary.set(*oldNode.node()->previousSibling(), boundary.offset() + offset, 0); + else if (boundary.container() == oldNode.node()->parentNode() && static_cast<int>(boundary.offset()) == oldNode.index()) + boundary.set(*oldNode.node()->previousSibling(), offset, 0); } void Range::textNodesMerged(NodeWithIndex& oldNode, unsigned offset) @@ -2156,30 +1700,42 @@ void Range::textNodesMerged(NodeWithIndex& oldNode, unsigned offset) static inline void boundaryTextNodesSplit(RangeBoundaryPoint& boundary, Text* oldNode) { - if (boundary.container() != oldNode) + auto* parent = oldNode->parentNode(); + if (boundary.container() == oldNode) { + unsigned splitOffset = oldNode->length(); + unsigned boundaryOffset = boundary.offset(); + if (boundaryOffset > splitOffset) { + if (parent) + boundary.set(*oldNode->nextSibling(), boundaryOffset - splitOffset, 0); + else + boundary.setOffset(splitOffset); + } return; - unsigned boundaryOffset = boundary.offset(); - if (boundaryOffset <= oldNode->length()) + } + if (!parent) return; - boundary.set(oldNode->nextSibling(), boundaryOffset - oldNode->length(), 0); + if (boundary.container() == parent && boundary.childBefore() == oldNode) { + auto* newChild = oldNode->nextSibling(); + ASSERT(newChild); + boundary.setToAfterChild(*newChild); + } } void Range::textNodeSplit(Text* oldNode) { ASSERT(oldNode); ASSERT(&oldNode->document() == &ownerDocument()); - ASSERT(oldNode->parentNode()); ASSERT(oldNode->isTextNode()); - ASSERT(oldNode->nextSibling()); - ASSERT(oldNode->nextSibling()->isTextNode()); + ASSERT(!oldNode->parentNode() || oldNode->nextSibling()); + ASSERT(!oldNode->parentNode() || oldNode->nextSibling()->isTextNode()); boundaryTextNodesSplit(m_start, oldNode); boundaryTextNodesSplit(m_end, oldNode); } -void Range::expand(const String& unit, ExceptionCode& ec) +ExceptionOr<void> Range::expand(const String& unit) { - VisiblePosition start(startPosition()); - VisiblePosition end(endPosition()); + VisiblePosition start { startPosition() }; + VisiblePosition end { endPosition() }; if (unit == "word") { start = startOfWord(start); end = endOfWord(end); @@ -2193,97 +1749,94 @@ void Range::expand(const String& unit, ExceptionCode& ec) start = startOfDocument(start); end = endOfDocument(end); } else - return; - setStart(start.deepEquivalent().containerNode(), start.deepEquivalent().computeOffsetInContainerNode(), ec); - setEnd(end.deepEquivalent().containerNode(), end.deepEquivalent().computeOffsetInContainerNode(), ec); + return { }; + + auto* startContainer = start.deepEquivalent().containerNode(); + if (!startContainer) + return Exception { TypeError }; + auto result = setStart(*startContainer, start.deepEquivalent().computeOffsetInContainerNode()); + if (result.hasException()) + return result.releaseException(); + auto* endContainer = end.deepEquivalent().containerNode(); + if (!endContainer) + return Exception { TypeError }; + return setEnd(*endContainer, end.deepEquivalent().computeOffsetInContainerNode()); } -PassRefPtr<ClientRectList> Range::getClientRects() const +Ref<ClientRectList> Range::getClientRects() const { - if (!m_start.container()) - return ClientRectList::create(); - - ownerDocument().updateLayoutIgnorePendingStylesheets(); - - Vector<FloatQuad> quads; - getBorderAndTextQuads(quads); - - return ClientRectList::create(quads); + return ClientRectList::create(borderAndTextQuads(CoordinateSpace::Client)); } -PassRefPtr<ClientRect> Range::getBoundingClientRect() const +Ref<ClientRect> Range::getBoundingClientRect() const { - return ClientRect::create(boundingRect()); + return ClientRect::create(boundingRect(CoordinateSpace::Client)); } -void Range::getBorderAndTextQuads(Vector<FloatQuad>& quads) const +Vector<FloatQuad> Range::borderAndTextQuads(CoordinateSpace space) const { - Node* startContainer = m_start.container(); - Node* endContainer = m_end.container(); + Vector<FloatQuad> quads; + + ownerDocument().updateLayoutIgnorePendingStylesheets(); + Node* stopNode = pastLastNode(); HashSet<Node*> selectedElementsSet; - for (Node* node = firstNode(); node != stopNode; node = NodeTraversal::next(node)) { - if (node->isElementNode()) + for (Node* node = firstNode(); node != stopNode; node = NodeTraversal::next(*node)) { + if (is<Element>(*node)) selectedElementsSet.add(node); } // Don't include elements that are only partially selected. - Node* lastNode = m_end.childBefore() ? m_end.childBefore() : endContainer; + Node* lastNode = m_end.childBefore() ? m_end.childBefore() : &endContainer(); for (Node* parent = lastNode->parentNode(); parent; parent = parent->parentNode()) selectedElementsSet.remove(parent); - for (Node* node = firstNode(); node != stopNode; node = NodeTraversal::next(node)) { - if (node->isElementNode() && selectedElementsSet.contains(node) && !selectedElementsSet.contains(node->parentNode())) { - if (RenderBoxModelObject* renderBoxModelObject = toElement(node)->renderBoxModelObject()) { + for (Node* node = firstNode(); node != stopNode; node = NodeTraversal::next(*node)) { + if (is<Element>(*node) && selectedElementsSet.contains(node) && !selectedElementsSet.contains(node->parentNode())) { + if (auto* renderer = downcast<Element>(*node).renderBoxModelObject()) { Vector<FloatQuad> elementQuads; - renderBoxModelObject->absoluteQuads(elementQuads); - ownerDocument().adjustFloatQuadsForScrollAndAbsoluteZoomAndFrameScale(elementQuads, renderBoxModelObject->style()); - + renderer->absoluteQuads(elementQuads); + if (space == CoordinateSpace::Client) + node->document().adjustFloatQuadsForScrollAndAbsoluteZoomAndFrameScale(elementQuads, renderer->style()); quads.appendVector(elementQuads); } - } else if (node->isTextNode()) { - if (RenderObject* renderer = toText(node)->renderer()) { - const RenderText& renderText = toRenderText(*renderer); - int startOffset = (node == startContainer) ? m_start.offset() : 0; - int endOffset = (node == endContainer) ? m_end.offset() : INT_MAX; - - auto textQuads = renderText.absoluteQuadsForRange(startOffset, endOffset); - ownerDocument().adjustFloatQuadsForScrollAndAbsoluteZoomAndFrameScale(textQuads, renderText.style()); - + } else if (is<Text>(*node)) { + if (auto* renderer = downcast<Text>(*node).renderer()) { + unsigned startOffset = node == &startContainer() ? m_start.offset() : 0; + unsigned endOffset = node == &endContainer() ? m_end.offset() : std::numeric_limits<unsigned>::max(); + auto textQuads = renderer->absoluteQuadsForRange(startOffset, endOffset); + if (space == CoordinateSpace::Client) + node->document().adjustFloatQuadsForScrollAndAbsoluteZoomAndFrameScale(textQuads, renderer->style()); quads.appendVector(textQuads); } } } + + return quads; } -FloatRect Range::boundingRect() const +FloatRect Range::boundingRect(CoordinateSpace space) const { - if (!m_start.container()) - return FloatRect(); - - ownerDocument().updateLayoutIgnorePendingStylesheets(); - - Vector<FloatQuad> quads; - getBorderAndTextQuads(quads); - if (quads.isEmpty()) - return FloatRect(); - FloatRect result; - for (size_t i = 0; i < quads.size(); ++i) - result.unite(quads[i].boundingBox()); - + for (auto& quad : borderAndTextQuads(space)) + result.unite(quad.boundingBox()); return result; } +FloatRect Range::absoluteBoundingRect() const +{ + return boundingRect(CoordinateSpace::Absolute); +} + } // namespace WebCore -#ifndef NDEBUG +#if ENABLE(TREE_DEBUGGING) void showTree(const WebCore::Range* range) { if (range && range->boundaryPointsValid()) { - range->startContainer()->showTreeAndMark(range->startContainer(), "S", range->endContainer(), "E"); + range->startContainer().showTreeAndMark(&range->startContainer(), "S", &range->endContainer(), "E"); fprintf(stderr, "start offset: %d, end offset: %d\n", range->startOffset(), range->endOffset()); } } diff --git a/Source/WebCore/dom/Range.h b/Source/WebCore/dom/Range.h index 877b860bd..5570b5520 100644 --- a/Source/WebCore/dom/Range.h +++ b/Source/WebCore/dom/Range.h @@ -22,10 +22,8 @@ * */ -#ifndef Range_h -#define Range_h +#pragma once -#include "ExceptionCodePlaceholder.h" #include "FloatRect.h" #include "FragmentScriptingPermission.h" #include "IntRect.h" @@ -44,81 +42,71 @@ class DocumentFragment; class FloatQuad; class Node; class NodeWithIndex; -class Text; -#if PLATFORM(IOS) class SelectionRect; +class Text; class VisiblePosition; -#endif class Range : public RefCounted<Range> { public: - static PassRefPtr<Range> create(Document&); - static PassRefPtr<Range> create(Document&, PassRefPtr<Node> startContainer, int startOffset, PassRefPtr<Node> endContainer, int endOffset); - static PassRefPtr<Range> create(Document&, const Position&, const Position&); - static PassRefPtr<Range> create(ScriptExecutionContext&); -#if PLATFORM(IOS) - // FIXME: Consider making this a static non-member, non-friend function. - static PassRefPtr<Range> create(Document&, const VisiblePosition&, const VisiblePosition&); -#endif - ~Range(); - - Document& ownerDocument() const { return const_cast<Document&>(m_ownerDocument.get()); } - - Node* startContainer() const { return m_start.container(); } - int startOffset() const { return m_start.offset(); } - Node* endContainer() const { return m_end.container(); } - int endOffset() const { return m_end.offset(); } - - Node* startContainer(ExceptionCode&) const; - int startOffset(ExceptionCode&) const; - Node* endContainer(ExceptionCode&) const; - int endOffset(ExceptionCode&) const; - bool collapsed(ExceptionCode&) const; - - Node* commonAncestorContainer(ExceptionCode&) const; - static Node* commonAncestorContainer(Node* containerA, Node* containerB); - void setStart(PassRefPtr<Node> container, int offset, ExceptionCode& = ASSERT_NO_EXCEPTION); - void setEnd(PassRefPtr<Node> container, int offset, ExceptionCode& = ASSERT_NO_EXCEPTION); - void collapse(bool toStart, ExceptionCode&); - bool isPointInRange(Node* refNode, int offset, ExceptionCode&); - short comparePoint(Node* refNode, int offset, ExceptionCode&) const; + WEBCORE_EXPORT static Ref<Range> create(Document&); + WEBCORE_EXPORT static Ref<Range> create(Document&, RefPtr<Node>&& startContainer, int startOffset, RefPtr<Node>&& endContainer, int endOffset); + WEBCORE_EXPORT static Ref<Range> create(Document&, const Position&, const Position&); + WEBCORE_EXPORT static Ref<Range> create(Document&, const VisiblePosition&, const VisiblePosition&); + WEBCORE_EXPORT ~Range(); + + Document& ownerDocument() const { return m_ownerDocument; } + + Node& startContainer() const { ASSERT(m_start.container()); return *m_start.container(); } + unsigned startOffset() const { return m_start.offset(); } + Node& endContainer() const { ASSERT(m_end.container()); return *m_end.container(); } + unsigned endOffset() const { return m_end.offset(); } + bool collapsed() const { return m_start == m_end; } + + Node* commonAncestorContainer() const { return commonAncestorContainer(&startContainer(), &endContainer()); } + WEBCORE_EXPORT static Node* commonAncestorContainer(Node* containerA, Node* containerB); + WEBCORE_EXPORT ExceptionOr<void> setStart(Ref<Node>&& container, unsigned offset); + WEBCORE_EXPORT ExceptionOr<void> setEnd(Ref<Node>&& container, unsigned offset); + WEBCORE_EXPORT void collapse(bool toStart); + WEBCORE_EXPORT ExceptionOr<bool> isPointInRange(Node& refNode, unsigned offset); + WEBCORE_EXPORT ExceptionOr<short> comparePoint(Node& refNode, unsigned offset) const; enum CompareResults { NODE_BEFORE, NODE_AFTER, NODE_BEFORE_AND_AFTER, NODE_INSIDE }; - CompareResults compareNode(Node* refNode, ExceptionCode&) const; + WEBCORE_EXPORT ExceptionOr<CompareResults> compareNode(Node& refNode) const; enum CompareHow { START_TO_START, START_TO_END, END_TO_END, END_TO_START }; - short compareBoundaryPoints(CompareHow, const Range* sourceRange, ExceptionCode&) const; - static short compareBoundaryPoints(Node* containerA, int offsetA, Node* containerB, int offsetB, ExceptionCode&); - static short compareBoundaryPoints(const RangeBoundaryPoint& boundaryA, const RangeBoundaryPoint& boundaryB, ExceptionCode&); - bool boundaryPointsValid() const; - bool intersectsNode(Node* refNode, ExceptionCode&); - void deleteContents(ExceptionCode&); - PassRefPtr<DocumentFragment> extractContents(ExceptionCode&); - PassRefPtr<DocumentFragment> cloneContents(ExceptionCode&); - void insertNode(PassRefPtr<Node>, ExceptionCode&); - String toString(ExceptionCode&) const; + WEBCORE_EXPORT ExceptionOr<short> compareBoundaryPoints(CompareHow, const Range& sourceRange) const; + WEBCORE_EXPORT ExceptionOr<short> compareBoundaryPointsForBindings(unsigned short compareHow, const Range& sourceRange) const; + static ExceptionOr<short> compareBoundaryPoints(Node* containerA, unsigned offsetA, Node* containerB, unsigned offsetB); + static ExceptionOr<short> compareBoundaryPoints(const RangeBoundaryPoint& boundaryA, const RangeBoundaryPoint& boundaryB); + WEBCORE_EXPORT bool boundaryPointsValid() const; + WEBCORE_EXPORT ExceptionOr<bool> intersectsNode(Node& refNode) const; + WEBCORE_EXPORT ExceptionOr<void> deleteContents(); + WEBCORE_EXPORT ExceptionOr<Ref<DocumentFragment>> extractContents(); + WEBCORE_EXPORT ExceptionOr<Ref<DocumentFragment>> cloneContents(); + WEBCORE_EXPORT ExceptionOr<void> insertNode(Ref<Node>&&); + WEBCORE_EXPORT String toString() const; String toHTML() const; - String text() const; + WEBCORE_EXPORT String text() const; - PassRefPtr<DocumentFragment> createContextualFragment(const String& html, ExceptionCode&); + WEBCORE_EXPORT ExceptionOr<Ref<DocumentFragment>> createContextualFragment(const String& html); - void detach(ExceptionCode&); - PassRefPtr<Range> cloneRange(ExceptionCode&) const; + WEBCORE_EXPORT void detach(); + WEBCORE_EXPORT Ref<Range> cloneRange() const; - void setStartAfter(Node*, ExceptionCode& = ASSERT_NO_EXCEPTION); - void setEndBefore(Node*, ExceptionCode& = ASSERT_NO_EXCEPTION); - void setEndAfter(Node*, ExceptionCode& = ASSERT_NO_EXCEPTION); - void selectNode(Node*, ExceptionCode& = ASSERT_NO_EXCEPTION); - void selectNodeContents(Node*, ExceptionCode&); - void surroundContents(PassRefPtr<Node>, ExceptionCode&); - void setStartBefore(Node*, ExceptionCode&); + WEBCORE_EXPORT ExceptionOr<void> setStartAfter(Node&); + WEBCORE_EXPORT ExceptionOr<void> setEndBefore(Node&); + WEBCORE_EXPORT ExceptionOr<void> setEndAfter(Node&); + WEBCORE_EXPORT ExceptionOr<void> selectNode(Node&); + WEBCORE_EXPORT ExceptionOr<void> selectNodeContents(Node&); + WEBCORE_EXPORT ExceptionOr<void> surroundContents(Node&); + WEBCORE_EXPORT ExceptionOr<void> setStartBefore(Node&); const Position startPosition() const { return m_start.toPosition(); } const Position endPosition() const { return m_end.toPosition(); } - void setStart(const Position&, ExceptionCode& = ASSERT_NO_EXCEPTION); - void setEnd(const Position&, ExceptionCode& = ASSERT_NO_EXCEPTION); + WEBCORE_EXPORT ExceptionOr<void> setStart(const Position&); + WEBCORE_EXPORT ExceptionOr<void> setEnd(const Position&); - Node* firstNode() const; - Node* pastLastNode() const; + WEBCORE_EXPORT Node* firstNode() const; + WEBCORE_EXPORT Node* pastLastNode() const; ShadowRoot* shadowRoot() const; @@ -129,20 +117,20 @@ public: }; // Not transform-friendly - void textRects(Vector<IntRect>&, bool useSelectionHeight = false, RangeInFixedPosition* = 0) const; - IntRect boundingBox() const; + WEBCORE_EXPORT void absoluteTextRects(Vector<IntRect>&, bool useSelectionHeight = false, RangeInFixedPosition* = nullptr) const; + WEBCORE_EXPORT IntRect absoluteBoundingBox() const; // Transform-friendly - void textQuads(Vector<FloatQuad>&, bool useSelectionHeight = false, RangeInFixedPosition* = 0) const; - void getBorderAndTextQuads(Vector<FloatQuad>&) const; - FloatRect boundingRect() const; + WEBCORE_EXPORT void absoluteTextQuads(Vector<FloatQuad>&, bool useSelectionHeight = false, RangeInFixedPosition* = nullptr) const; + WEBCORE_EXPORT FloatRect absoluteBoundingRect() const; #if PLATFORM(IOS) - void collectSelectionRects(Vector<SelectionRect>&); + WEBCORE_EXPORT void collectSelectionRects(Vector<SelectionRect>&); + WEBCORE_EXPORT int collectSelectionRectsWithoutUnionInteriorLines(Vector<SelectionRect>&); #endif void nodeChildrenChanged(ContainerNode&); void nodeChildrenWillBeRemoved(ContainerNode&); - void nodeWillBeRemoved(Node*); + void nodeWillBeRemoved(Node&); void textInserted(Node*, unsigned offset, unsigned length); void textRemoved(Node*, unsigned offset, unsigned length); @@ -152,49 +140,50 @@ public: // Expand range to a unit (word or sentence or block or document) boundary. // Please refer to https://bugs.webkit.org/show_bug.cgi?id=27632 comment #5 // for details. - void expand(const String&, ExceptionCode&); + WEBCORE_EXPORT ExceptionOr<void> expand(const String&); - PassRefPtr<ClientRectList> getClientRects() const; - PassRefPtr<ClientRect> getBoundingClientRect() const; + Ref<ClientRectList> getClientRects() const; + Ref<ClientRect> getBoundingClientRect() const; -#ifndef NDEBUG +#if ENABLE(TREE_DEBUGGING) void formatForDebugger(char* buffer, unsigned length) const; #endif + WEBCORE_EXPORT bool contains(const Range&) const; + bool contains(const VisiblePosition&) const; + + enum ActionType { Delete, Extract, Clone }; + private: explicit Range(Document&); - Range(Document&, PassRefPtr<Node> startContainer, int startOffset, PassRefPtr<Node> endContainer, int endOffset); + Range(Document&, Node* startContainer, int startOffset, Node* endContainer, int endOffset); void setDocument(Document&); + ExceptionOr<Node*> checkNodeWOffset(Node&, unsigned offset) const; + ExceptionOr<RefPtr<DocumentFragment>> processContents(ActionType); - Node* checkNodeWOffset(Node*, int offset, ExceptionCode&) const; - void checkNodeBA(Node*, ExceptionCode&) const; - void checkDeleteExtract(ExceptionCode&); - bool containedByReadOnly() const; - int maxStartOffset() const; - int maxEndOffset() const; - - enum ActionType { Delete, Extract, Clone }; - PassRefPtr<DocumentFragment> processContents(ActionType, ExceptionCode&); - static PassRefPtr<Node> processContentsBetweenOffsets(ActionType, PassRefPtr<DocumentFragment>, Node*, unsigned startOffset, unsigned endOffset, ExceptionCode&); - static void processNodes(ActionType, Vector<RefPtr<Node>>&, PassRefPtr<Node> oldContainer, PassRefPtr<Node> newContainer, ExceptionCode&); - enum ContentsProcessDirection { ProcessContentsForward, ProcessContentsBackward }; - static PassRefPtr<Node> processAncestorsAndTheirSiblings(ActionType, Node* container, ContentsProcessDirection, PassRefPtr<Node> clonedContainer, Node* commonRoot, ExceptionCode&); + enum class CoordinateSpace { Absolute, Client }; + Vector<FloatQuad> borderAndTextQuads(CoordinateSpace) const; + FloatRect boundingRect(CoordinateSpace) const; Ref<Document> m_ownerDocument; RangeBoundaryPoint m_start; RangeBoundaryPoint m_end; }; -PassRefPtr<Range> rangeOfContents(Node&); +WEBCORE_EXPORT Ref<Range> rangeOfContents(Node&); -bool areRangesEqual(const Range*, const Range*); +WEBCORE_EXPORT bool areRangesEqual(const Range*, const Range*); +bool rangesOverlap(const Range*, const Range*); + +inline bool documentOrderComparator(const Node* a, const Node* b) +{ + return Range::compareBoundaryPoints(const_cast<Node*>(a), 0, const_cast<Node*>(b), 0).releaseReturnValue() < 0; +} } // namespace -#ifndef NDEBUG -// Outside the WebCore namespace for ease of invocation from gdb. +#if ENABLE(TREE_DEBUGGING) +// Outside the WebCore namespace for ease of invocation from the debugger. void showTree(const WebCore::Range*); #endif - -#endif diff --git a/Source/WebCore/dom/Range.idl b/Source/WebCore/dom/Range.idl index bd47335f4..5e8539391 100644 --- a/Source/WebCore/dom/Range.idl +++ b/Source/WebCore/dom/Range.idl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006 Apple Inc. * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> * * This library is free software; you can redistribute it and/or @@ -21,81 +21,62 @@ // Introduced in DOM Level 2: [ Constructor, - ConstructorCallWith=ScriptExecutionContext, - ImplementationLacksVTable + ConstructorCallWith=Document, + ImplementationLacksVTable, + ExportMacro=WEBCORE_EXPORT, ] interface Range { - - [GetterRaisesException] readonly attribute Node startContainer; - [GetterRaisesException] readonly attribute long startOffset; - [GetterRaisesException] readonly attribute Node endContainer; - [GetterRaisesException] readonly attribute long endOffset; - [GetterRaisesException] readonly attribute boolean collapsed; - [GetterRaisesException] readonly attribute Node commonAncestorContainer; - - [ObjCLegacyUnnamedParameters, RaisesException] void setStart([Default=Undefined] optional Node refNode, - [Default=Undefined] optional long offset); - [ObjCLegacyUnnamedParameters, RaisesException] void setEnd([Default=Undefined] optional Node refNode, - [Default=Undefined] optional long offset); - [RaisesException] void setStartBefore([Default=Undefined] optional Node refNode); - [RaisesException] void setStartAfter([Default=Undefined] optional Node refNode); - [RaisesException] void setEndBefore([Default=Undefined] optional Node refNode); - [RaisesException] void setEndAfter([Default=Undefined] optional Node refNode); - [RaisesException] void collapse([Default=Undefined] optional boolean toStart); - [RaisesException] void selectNode([Default=Undefined] optional Node refNode); - [RaisesException] void selectNodeContents([Default=Undefined] optional Node refNode); + readonly attribute Node startContainer; + readonly attribute long startOffset; + readonly attribute Node endContainer; + readonly attribute long endOffset; + readonly attribute boolean collapsed; + readonly attribute Node commonAncestorContainer; + + [MayThrowException] void setStart(Node refNode, unsigned long offset); + [MayThrowException] void setEnd(Node refNode, unsigned long offset); + + [MayThrowException] void setStartBefore(Node refNode); + [MayThrowException] void setStartAfter(Node refNode); + [MayThrowException] void setEndBefore(Node refNode); + [MayThrowException] void setEndAfter(Node refNode); + void collapse(optional boolean toStart = false); + [MayThrowException] void expand(optional DOMString unit = ""); + [MayThrowException] void selectNode(Node refNode); + [MayThrowException] void selectNodeContents(Node refNode); // CompareHow const unsigned short START_TO_START = 0; - const unsigned short START_TO_END = 1; - const unsigned short END_TO_END = 2; - const unsigned short END_TO_START = 3; - - [ObjCLegacyUnnamedParameters, RaisesException] short compareBoundaryPoints([Default=Undefined] optional CompareHow how, - [Default=Undefined] optional Range sourceRange); + const unsigned short START_TO_END = 1; + const unsigned short END_TO_END = 2; + const unsigned short END_TO_START = 3; - [RaisesException] void deleteContents(); - [RaisesException] DocumentFragment extractContents(); - [RaisesException] DocumentFragment cloneContents(); - [RaisesException] void insertNode([Default=Undefined] optional Node newNode); - [RaisesException] void surroundContents([Default=Undefined] optional Node newParent); - [RaisesException] Range cloneRange(); - [RaisesException] DOMString toString(); + [MayThrowException, ImplementedAs=compareBoundaryPointsForBindings] short compareBoundaryPoints(unsigned short how, Range sourceRange); - [RaisesException] void detach(); + [CEReactions, MayThrowException] void deleteContents(); + [CEReactions, MayThrowException, NewObject] DocumentFragment extractContents(); + [CEReactions, MayThrowException, NewObject] DocumentFragment cloneContents(); + [CEReactions, MayThrowException] void insertNode(Node newNode); + [CEReactions, MayThrowException] void surroundContents(Node newParent); + [NewObject] Range cloneRange(); + DOMString toString(); -#if defined(LANGUAGE_JAVASCRIPT) || LANGUAGE_JAVASCRIPT - // CSSOM View Module API extensions + void detach(); ClientRectList getClientRects(); ClientRect getBoundingClientRect(); -#endif - - // extensions - - [RaisesException] DocumentFragment createContextualFragment([Default=Undefined] optional DOMString html); - // WebKit extensions + [CEReactions, MayThrowException, NewObject] DocumentFragment createContextualFragment(DOMString html); - [RaisesException] boolean intersectsNode([Default=Undefined] optional Node refNode); - - [RaisesException] short compareNode([Default=Undefined] optional Node refNode); + [MayThrowException] short compareNode(Node refNode); // CompareResults - const unsigned short NODE_BEFORE = 0; - const unsigned short NODE_AFTER = 1; + const unsigned short NODE_BEFORE = 0; + const unsigned short NODE_AFTER = 1; const unsigned short NODE_BEFORE_AND_AFTER = 2; - const unsigned short NODE_INSIDE = 3; - - [RaisesException] short comparePoint([Default=Undefined] optional Node refNode, - [Default=Undefined] optional long offset); + const unsigned short NODE_INSIDE = 3; - [RaisesException] boolean isPointInRange([Default=Undefined] optional Node refNode, - [Default=Undefined] optional long offset); + [MayThrowException] boolean intersectsNode(Node refNode); - [RaisesException] void expand([Default=Undefined] optional DOMString unit); - -#if !defined(LANGUAGE_JAVASCRIPT) || !LANGUAGE_JAVASCRIPT - readonly attribute DOMString text; -#endif + [MayThrowException] short comparePoint(Node refNode, unsigned long offset); + [MayThrowException] boolean isPointInRange(Node refNode, unsigned long offset); }; - diff --git a/Source/WebCore/dom/RangeBoundaryPoint.h b/Source/WebCore/dom/RangeBoundaryPoint.h index 99250198a..71c92cdfc 100644 --- a/Source/WebCore/dom/RangeBoundaryPoint.h +++ b/Source/WebCore/dom/RangeBoundaryPoint.h @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef RangeBoundaryPoint_h -#define RangeBoundaryPoint_h +#pragma once #include "Node.h" #include "Position.h" @@ -33,41 +32,38 @@ namespace WebCore { class RangeBoundaryPoint { public: - explicit RangeBoundaryPoint(PassRefPtr<Node> container); + explicit RangeBoundaryPoint(Node* container); explicit RangeBoundaryPoint(const RangeBoundaryPoint&); const Position toPosition() const; Node* container() const; - int offset() const; + unsigned offset() const; Node* childBefore() const; void clear(); - void set(PassRefPtr<Node> container, int offset, Node* childBefore); - void setOffset(int offset); + void set(Ref<Node>&& container, unsigned offset, Node* childBefore); + void setOffset(unsigned); - void setToBeforeChild(Node*); - void setToStartOfNode(PassRefPtr<Node>); - void setToEndOfNode(PassRefPtr<Node>); + void setToBeforeChild(Node&); + void setToAfterChild(Node&); + void setToStartOfNode(Ref<Node>&&); + void setToEndOfNode(Ref<Node>&&); void childBeforeWillBeRemoved(); void invalidateOffset() const; void ensureOffsetIsValid() const; private: - static const int invalidOffset = -1; - RefPtr<Node> m_containerNode; - mutable int m_offsetInContainer; + mutable std::optional<unsigned> m_offsetInContainer { 0 }; RefPtr<Node> m_childBeforeBoundary; }; -inline RangeBoundaryPoint::RangeBoundaryPoint(PassRefPtr<Node> container) +inline RangeBoundaryPoint::RangeBoundaryPoint(Node* container) : m_containerNode(container) - , m_offsetInContainer(0) - , m_childBeforeBoundary(0) { ASSERT(m_containerNode); } @@ -91,94 +87,97 @@ inline Node* RangeBoundaryPoint::childBefore() const inline void RangeBoundaryPoint::ensureOffsetIsValid() const { - if (m_offsetInContainer >= 0) + if (m_offsetInContainer) return; ASSERT(m_childBeforeBoundary); - m_offsetInContainer = m_childBeforeBoundary->nodeIndex() + 1; + m_offsetInContainer = m_childBeforeBoundary->computeNodeIndex() + 1; } inline const Position RangeBoundaryPoint::toPosition() const { ensureOffsetIsValid(); - return createLegacyEditingPosition(m_containerNode.get(), m_offsetInContainer); + return createLegacyEditingPosition(m_containerNode.get(), m_offsetInContainer.value()); } -inline int RangeBoundaryPoint::offset() const +inline unsigned RangeBoundaryPoint::offset() const { ensureOffsetIsValid(); - return m_offsetInContainer; + return m_offsetInContainer.value(); } inline void RangeBoundaryPoint::clear() { - m_containerNode.clear(); + m_containerNode = nullptr; m_offsetInContainer = 0; - m_childBeforeBoundary = 0; + m_childBeforeBoundary = nullptr; } -inline void RangeBoundaryPoint::set(PassRefPtr<Node> container, int offset, Node* childBefore) +inline void RangeBoundaryPoint::set(Ref<Node>&& container, unsigned offset, Node* childBefore) { - ASSERT(container); - ASSERT(offset >= 0); - ASSERT(childBefore == (offset ? container->childNode(offset - 1) : 0)); - m_containerNode = container; + ASSERT(childBefore == (offset ? container->traverseToChildAt(offset - 1) : 0)); + m_containerNode = WTFMove(container); m_offsetInContainer = offset; m_childBeforeBoundary = childBefore; } -inline void RangeBoundaryPoint::setOffset(int offset) +inline void RangeBoundaryPoint::setOffset(unsigned offset) { ASSERT(m_containerNode); ASSERT(m_containerNode->offsetInCharacters()); - ASSERT(m_offsetInContainer >= 0); + ASSERT(m_offsetInContainer); ASSERT(!m_childBeforeBoundary); m_offsetInContainer = offset; } -inline void RangeBoundaryPoint::setToBeforeChild(Node* child) +inline void RangeBoundaryPoint::setToBeforeChild(Node& child) +{ + ASSERT(child.parentNode()); + m_childBeforeBoundary = child.previousSibling(); + m_containerNode = child.parentNode(); + m_offsetInContainer = m_childBeforeBoundary ? std::nullopt : std::optional<unsigned>(0); +} + +inline void RangeBoundaryPoint::setToAfterChild(Node& child) { - ASSERT(child); - ASSERT(child->parentNode()); - m_childBeforeBoundary = child->previousSibling(); - m_containerNode = child->parentNode(); - m_offsetInContainer = m_childBeforeBoundary ? invalidOffset : 0; + ASSERT(child.parentNode()); + m_childBeforeBoundary = &child; + m_containerNode = child.parentNode(); + m_offsetInContainer = m_childBeforeBoundary ? std::nullopt : std::optional<unsigned>(0); } -inline void RangeBoundaryPoint::setToStartOfNode(PassRefPtr<Node> container) +inline void RangeBoundaryPoint::setToStartOfNode(Ref<Node>&& container) { - ASSERT(container); - m_containerNode = container; + m_containerNode = WTFMove(container); m_offsetInContainer = 0; - m_childBeforeBoundary = 0; + m_childBeforeBoundary = nullptr; } -inline void RangeBoundaryPoint::setToEndOfNode(PassRefPtr<Node> container) +inline void RangeBoundaryPoint::setToEndOfNode(Ref<Node>&& container) { - ASSERT(container); - m_containerNode = container; + m_containerNode = WTFMove(container); if (m_containerNode->offsetInCharacters()) { m_offsetInContainer = m_containerNode->maxCharacterOffset(); - m_childBeforeBoundary = 0; + m_childBeforeBoundary = nullptr; } else { m_childBeforeBoundary = m_containerNode->lastChild(); - m_offsetInContainer = m_childBeforeBoundary ? invalidOffset : 0; + m_offsetInContainer = m_childBeforeBoundary ? std::nullopt : std::optional<unsigned>(0); } } inline void RangeBoundaryPoint::childBeforeWillBeRemoved() { - ASSERT(m_offsetInContainer); + ASSERT(!m_offsetInContainer || m_offsetInContainer.value()); m_childBeforeBoundary = m_childBeforeBoundary->previousSibling(); if (!m_childBeforeBoundary) m_offsetInContainer = 0; - else if (m_offsetInContainer > 0) - --m_offsetInContainer; + else if (m_offsetInContainer && m_offsetInContainer.value() > 0) + --(m_offsetInContainer.value()); } inline void RangeBoundaryPoint::invalidateOffset() const { - m_offsetInContainer = invalidOffset; + m_offsetInContainer = std::nullopt; } inline bool operator==(const RangeBoundaryPoint& a, const RangeBoundaryPoint& b) @@ -195,6 +194,4 @@ inline bool operator==(const RangeBoundaryPoint& a, const RangeBoundaryPoint& b) return true; } -} - -#endif +} // namespace WebCore diff --git a/Source/WebCore/dom/RangeException.cpp b/Source/WebCore/dom/RangeException.cpp deleted file mode 100644 index f9d08a31a..000000000 --- a/Source/WebCore/dom/RangeException.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY GOOGLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "RangeException.h" - -namespace WebCore { - -static struct RangeExceptionNameDescription { - const char* const name; - const char* const description; -} rangeExceptions[] = { - { "BAD_BOUNDARYPOINTS_ERR", "The boundary-points of a Range did not meet specific requirements." }, - { "INVALID_NODE_TYPE_ERR", "The container of an boundary-point of a Range was being set to either a node of an invalid type or a node with an ancestor of an invalid type." } -}; - -bool RangeException::initializeDescription(ExceptionCode ec, ExceptionCodeDescription* description) -{ - if (ec < RangeExceptionOffset || ec > RangeExceptionMax) - return false; - - description->typeName = "DOM Range"; - description->code = ec - RangeExceptionOffset; - description->type = RangeExceptionType; - - size_t tableSize = WTF_ARRAY_LENGTH(rangeExceptions); - size_t tableIndex = ec - BAD_BOUNDARYPOINTS_ERR; - - description->name = tableIndex < tableSize ? rangeExceptions[tableIndex].name : 0; - description->description = tableIndex < tableSize ? rangeExceptions[tableIndex].description : 0; - - return true; -} - -} // namespace WebCore diff --git a/Source/WebCore/dom/RangeException.h b/Source/WebCore/dom/RangeException.h deleted file mode 100644 index 45f8c9375..000000000 --- a/Source/WebCore/dom/RangeException.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * (C) 1999 Lars Knoll (knoll@kde.org) - * (C) 2000 Gunnstein Lye (gunnstein@netcom.no) - * (C) 2000 Frederik Holljen (frederik.holljen@hig.no) - * (C) 2001 Peter Kelly (pmk@post.com) - * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef RangeException_h -#define RangeException_h - -#include "ExceptionBase.h" - -namespace WebCore { - -class RangeException : public ExceptionBase { -public: - static PassRefPtr<RangeException> create(const ExceptionCodeDescription& description) - { - return adoptRef(new RangeException(description)); - } - - static const int RangeExceptionOffset = 200; - static const int RangeExceptionMax = 299; - - enum RangeExceptionCode { - BAD_BOUNDARYPOINTS_ERR = RangeExceptionOffset + 1, - INVALID_NODE_TYPE_ERR - }; - - static bool initializeDescription(ExceptionCode, ExceptionCodeDescription*); - -private: - explicit RangeException(const ExceptionCodeDescription& description) - : ExceptionBase(description) - { - } -}; - -} // namespace WebCore - -#endif // RangeException_h diff --git a/Source/WebCore/dom/RangeException.idl b/Source/WebCore/dom/RangeException.idl deleted file mode 100644 index 6f600f269..000000000 --- a/Source/WebCore/dom/RangeException.idl +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -[ - DoNotCheckConstants, - ImplementationLacksVTable, -] exception RangeException { - - readonly attribute unsigned short code; - readonly attribute DOMString name; - readonly attribute DOMString message; - -#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT - [NotEnumerable] DOMString toString(); -#endif - - // DOM Level 2 - - const unsigned short BAD_BOUNDARYPOINTS_ERR = 1; - const unsigned short INVALID_NODE_TYPE_ERR = 2; -}; - diff --git a/Source/WebCore/dom/RawDataDocumentParser.h b/Source/WebCore/dom/RawDataDocumentParser.h index d62d18042..094ec87f6 100644 --- a/Source/WebCore/dom/RawDataDocumentParser.h +++ b/Source/WebCore/dom/RawDataDocumentParser.h @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef RawDataDocumentParser_h -#define RawDataDocumentParser_h +#pragma once #include "DocumentParser.h" @@ -37,31 +36,29 @@ protected: { } - virtual void finish() + void finish() override { if (!isStopped()) document()->finishedParsing(); } private: - virtual void flush(DocumentWriter& writer) + void flush(DocumentWriter& writer) override { // Make sure appendBytes is called at least once. appendBytes(writer, 0, 0); } - virtual void insert(const SegmentedString&) + void insert(SegmentedString&&) override { // <https://bugs.webkit.org/show_bug.cgi?id=25397>: JS code can always call document.write, we need to handle it. ASSERT_NOT_REACHED(); } - virtual void append(PassRefPtr<StringImpl>) + void append(RefPtr<StringImpl>&&) override { ASSERT_NOT_REACHED(); } }; -}; - -#endif // RawDataDocumentParser_h +} // namespace WebCore diff --git a/Source/WebCore/dom/RegisteredEventListener.cpp b/Source/WebCore/dom/RegisteredEventListener.cpp deleted file mode 100644 index 3adaca378..000000000 --- a/Source/WebCore/dom/RegisteredEventListener.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2001 Peter Kelly (pmk@post.com) - * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) - * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - * Copyright (C) 2003, 2005, 2006, 2008 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "config.h" -#include "RegisteredEventListener.h" - - -namespace WebCore { - -} // namespace WebCore diff --git a/Source/WebCore/dom/RegisteredEventListener.h b/Source/WebCore/dom/RegisteredEventListener.h index 12483fa61..2a58a3fa0 100644 --- a/Source/WebCore/dom/RegisteredEventListener.h +++ b/Source/WebCore/dom/RegisteredEventListener.h @@ -21,31 +21,55 @@ * */ -#ifndef RegisteredEventListener_h -#define RegisteredEventListener_h +#pragma once #include "EventListener.h" #include <wtf/RefPtr.h> namespace WebCore { - class RegisteredEventListener { - public: - RegisteredEventListener(PassRefPtr<EventListener> listener, bool useCapture) - : listener(listener) - , useCapture(useCapture) - { - } +// https://dom.spec.whatwg.org/#concept-event-listener +class RegisteredEventListener : public RefCounted<RegisteredEventListener> { +public: + struct Options { + Options(bool capture = false, bool passive = false, bool once = false) + : capture(capture) + , passive(passive) + , once(once) + { } - RefPtr<EventListener> listener; - bool useCapture; + bool capture; + bool passive; + bool once; }; - - inline bool operator==(const RegisteredEventListener& a, const RegisteredEventListener& b) + + static Ref<RegisteredEventListener> create(Ref<EventListener>&& listener, const Options& options) { - return *a.listener == *b.listener && a.useCapture == b.useCapture; + return adoptRef(*new RegisteredEventListener(WTFMove(listener), options)); } -} // namespace WebCore + EventListener& callback() const { return m_callback; } + bool useCapture() const { return m_useCapture; } + bool isPassive() const { return m_isPassive; } + bool isOnce() const { return m_isOnce; } + bool wasRemoved() const { return m_wasRemoved; } + + void markAsRemoved() { m_wasRemoved = true; } + +private: + RegisteredEventListener(Ref<EventListener>&& listener, const Options& options) + : m_callback(WTFMove(listener)) + , m_useCapture(options.capture) + , m_isPassive(options.passive) + , m_isOnce(options.once) + { + } -#endif // RegisteredEventListener_h + Ref<EventListener> m_callback; + bool m_useCapture { false }; + bool m_isPassive { false }; + bool m_isOnce { false }; + bool m_wasRemoved { false }; +}; + +} // namespace WebCore diff --git a/Source/WebCore/dom/RenderedDocumentMarker.h b/Source/WebCore/dom/RenderedDocumentMarker.h index 7e8fe2b55..89e6493b7 100644 --- a/Source/WebCore/dom/RenderedDocumentMarker.h +++ b/Source/WebCore/dom/RenderedDocumentMarker.h @@ -24,49 +24,53 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef RenderedDocumentMarker_h -#define RenderedDocumentMarker_h +#pragma once #include "DocumentMarker.h" +#include <wtf/Vector.h> namespace WebCore { class RenderedDocumentMarker : public DocumentMarker { public: - explicit RenderedDocumentMarker(const DocumentMarker& marker) - : DocumentMarker(marker), m_renderedRect(invalidMarkerRect()) + : DocumentMarker(marker) { } - bool isRendered() const { return invalidMarkerRect() != m_renderedRect; } - bool contains(const LayoutPoint& point) const { return isRendered() && m_renderedRect.contains(point); } - void setRenderedRect(const LayoutRect& r) { m_renderedRect = r; } - const LayoutRect& renderedRect() const { return m_renderedRect; } - void invalidate(const LayoutRect&); - void invalidate() { m_renderedRect = invalidMarkerRect(); } + bool contains(const FloatPoint& point) const + { + ASSERT(m_isValid); + for (const auto& rect : m_rects) { + if (rect.contains(point)) + return true; + } + return false; + } -private: - static const LayoutRect& invalidMarkerRect() + void setUnclippedAbsoluteRects(Vector<FloatRect>& rects) { - static const LayoutRect rect = LayoutRect(-1, -1, -1, -1); - return rect; + m_isValid = true; + m_rects = rects; } - LayoutRect m_renderedRect; -}; + const Vector<FloatRect, 1>& unclippedAbsoluteRects() const + { + ASSERT(m_isValid); + return m_rects; + } -inline void RenderedDocumentMarker::invalidate(const LayoutRect& r) -{ - if (m_renderedRect.intersects(r)) - invalidate(); -} + void invalidate() + { + m_isValid = false; + m_rects.clear(); + } -inline RenderedDocumentMarker* toRenderedDocumentMarker(DocumentMarker* marker) -{ - return static_cast<RenderedDocumentMarker*>(marker); -} + bool isValid() const { return m_isValid; } -} // namespace +private: + Vector<FloatRect, 1> m_rects; + bool m_isValid { false }; +}; -#endif +} // namespace WebCore diff --git a/Source/WebCore/dom/RequestAnimationFrameCallback.h b/Source/WebCore/dom/RequestAnimationFrameCallback.h index 52580dc53..86690b076 100644 --- a/Source/WebCore/dom/RequestAnimationFrameCallback.h +++ b/Source/WebCore/dom/RequestAnimationFrameCallback.h @@ -28,8 +28,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef RequestAnimationFrameCallback_h -#define RequestAnimationFrameCallback_h +#pragma once #include <wtf/RefCounted.h> @@ -45,6 +44,4 @@ public: bool m_useLegacyTimeBase; }; -} - -#endif // RequestAnimationFrameCallback_h +} // namespace WebCore diff --git a/Source/WebCore/dom/RequestAnimationFrameCallback.idl b/Source/WebCore/dom/RequestAnimationFrameCallback.idl index 9c3bfec60..f750aa644 100644 --- a/Source/WebCore/dom/RequestAnimationFrameCallback.idl +++ b/Source/WebCore/dom/RequestAnimationFrameCallback.idl @@ -28,10 +28,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -[ - Conditional=REQUEST_ANIMATION_FRAME, -] callback interface RequestAnimationFrameCallback{ - // highResTime is passed as high resolution timestamp, see - // http://www.w3.org/TR/hr-time/ for details. - [Custom] boolean handleEvent(double highResTime); -}; +// highResTime is passed as high resolution timestamp, see +// http://www.w3.org/TR/hr-time/ for details. + +callback RequestAnimationFrameCallback = void (unrestricted double highResTime); diff --git a/Source/WebCore/dom/ScopedEventQueue.cpp b/Source/WebCore/dom/ScopedEventQueue.cpp index 503843bc8..8fb659f8e 100644 --- a/Source/WebCore/dom/ScopedEventQueue.cpp +++ b/Source/WebCore/dom/ScopedEventQueue.cpp @@ -37,55 +37,43 @@ namespace WebCore { -ScopedEventQueue::ScopedEventQueue() - : m_scopingLevel(0) -{ -} - -ScopedEventQueue::~ScopedEventQueue() -{ - ASSERT(!m_scopingLevel); - ASSERT(!m_queuedEvents.size()); -} - -ScopedEventQueue& ScopedEventQueue::instance() +ScopedEventQueue& ScopedEventQueue::singleton() { static NeverDestroyed<ScopedEventQueue> scopedEventQueue; return scopedEventQueue; } -void ScopedEventQueue::enqueueEvent(PassRefPtr<Event> event) +void ScopedEventQueue::enqueueEvent(Ref<Event>&& event) { if (m_scopingLevel) - m_queuedEvents.append(event); + m_queuedEvents.append(WTFMove(event)); else dispatchEvent(event); } -void ScopedEventQueue::dispatchEvent(PassRefPtr<Event> event) const +void ScopedEventQueue::dispatchEvent(Event& event) const { - ASSERT(event->target()); - // Store the target in a local variable to avoid possibly dereferencing a nullified PassRefPtr after it's passed on. - Node* node = event->target()->toNode(); - EventDispatcher::dispatchEvent(node, event); + ASSERT(event.target()); + ASSERT(event.target()->toNode()); + EventDispatcher::dispatchEvent(*event.target()->toNode(), event); } void ScopedEventQueue::dispatchAllEvents() { - Vector<RefPtr<Event>> queuedEvents = std::move(m_queuedEvents); - for (size_t i = 0; i < queuedEvents.size(); i++) - dispatchEvent(queuedEvents[i].release()); + Vector<Ref<Event>> queuedEvents = WTFMove(m_queuedEvents); + for (auto& queuedEvent : queuedEvents) + dispatchEvent(queuedEvent); } void ScopedEventQueue::incrementScopingLevel() { - m_scopingLevel++; + ++m_scopingLevel; } void ScopedEventQueue::decrementScopingLevel() { ASSERT(m_scopingLevel); - m_scopingLevel--; + --m_scopingLevel; if (!m_scopingLevel) dispatchAllEvents(); } diff --git a/Source/WebCore/dom/ScopedEventQueue.h b/Source/WebCore/dom/ScopedEventQueue.h index 90465e626..8a9dcad6a 100644 --- a/Source/WebCore/dom/ScopedEventQueue.h +++ b/Source/WebCore/dom/ScopedEventQueue.h @@ -28,12 +28,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ScopedEventQueue_h -#define ScopedEventQueue_h +#pragma once #include <wtf/NeverDestroyed.h> #include <wtf/Noncopyable.h> -#include <wtf/PassRefPtr.h> #include <wtf/RefPtr.h> #include <wtf/Vector.h> @@ -45,20 +43,20 @@ class EventQueueScope; class ScopedEventQueue { WTF_MAKE_NONCOPYABLE(ScopedEventQueue); WTF_MAKE_FAST_ALLOCATED; public: - static ScopedEventQueue& instance(); - void enqueueEvent(PassRefPtr<Event>); + static ScopedEventQueue& singleton(); + void enqueueEvent(Ref<Event>&&); private: - ScopedEventQueue(); - ~ScopedEventQueue(); + ScopedEventQueue() = default; + ~ScopedEventQueue() = delete; - void dispatchEvent(PassRefPtr<Event>) const; + void dispatchEvent(Event&) const; void dispatchAllEvents(); void incrementScopingLevel(); void decrementScopingLevel(); - Vector<RefPtr<Event>> m_queuedEvents; - unsigned m_scopingLevel; + Vector<Ref<Event>> m_queuedEvents; + unsigned m_scopingLevel { 0 }; friend class WTF::NeverDestroyed<WebCore::ScopedEventQueue>; friend class EventQueueScope; @@ -67,10 +65,8 @@ private: class EventQueueScope { WTF_MAKE_NONCOPYABLE(EventQueueScope); public: - EventQueueScope() { ScopedEventQueue::instance().incrementScopingLevel(); } - ~EventQueueScope() { ScopedEventQueue::instance().decrementScopingLevel(); } + EventQueueScope() { ScopedEventQueue::singleton().incrementScopingLevel(); } + ~EventQueueScope() { ScopedEventQueue::singleton().decrementScopingLevel(); } }; -} - -#endif // ScopedEventQueue_h +} // namespace WebCore diff --git a/Source/WebCore/dom/ScriptElement.cpp b/Source/WebCore/dom/ScriptElement.cpp index 651cc5806..cf51d40d5 100644 --- a/Source/WebCore/dom/ScriptElement.cpp +++ b/Source/WebCore/dom/ScriptElement.cpp @@ -2,7 +2,7 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003-2017 Apple Inc. All rights reserved. * Copyright (C) 2008 Nikolas Zimmermann <zimmermann@kde.org> * * This library is free software; you can redistribute it and/or @@ -24,43 +24,39 @@ #include "config.h" #include "ScriptElement.h" -#include "CachedScript.h" #include "CachedResourceLoader.h" #include "CachedResourceRequest.h" +#include "CachedScript.h" #include "ContentSecurityPolicy.h" #include "CrossOriginAccessControl.h" #include "CurrentScriptIncrementer.h" #include "Event.h" +#include "EventNames.h" #include "Frame.h" #include "FrameLoader.h" #include "HTMLNames.h" #include "HTMLParserIdioms.h" #include "IgnoreDestructiveWriteCountIncrementer.h" +#include "InlineClassicScript.h" +#include "LoadableClassicScript.h" +#include "LoadableModuleScript.h" #include "MIMETypeRegistry.h" -#include "Page.h" -#include "ScriptCallStack.h" +#include "NoEventDispatchAssertion.h" +#include "PendingScript.h" +#include "SVGScriptElement.h" #include "ScriptController.h" #include "ScriptRunner.h" #include "ScriptSourceCode.h" #include "ScriptableDocumentParser.h" -#include "SecurityOrigin.h" -#include "Settings.h" #include "TextNodeTraversal.h" -#include <bindings/ScriptValue.h> #include <wtf/StdLibExtras.h> #include <wtf/text/StringBuilder.h> #include <wtf/text/StringHash.h> -#if ENABLE(SVG) -#include "SVGNames.h" -#include "SVGScriptElement.h" -#endif - namespace WebCore { -ScriptElement::ScriptElement(Element* element, bool parserInserted, bool alreadyStarted) +ScriptElement::ScriptElement(Element& element, bool parserInserted, bool alreadyStarted) : m_element(element) - , m_cachedScript(0) , m_startLineNumber(WTF::OrdinalNumber::beforeFirst()) , m_parserInserted(parserInserted) , m_isExternalScript(false) @@ -71,38 +67,32 @@ ScriptElement::ScriptElement(Element* element, bool parserInserted, bool already , m_willExecuteWhenDocumentFinishedParsing(false) , m_forceAsync(!parserInserted) , m_willExecuteInOrder(false) - , m_requestUsesAccessControl(false) + , m_isModuleScript(false) { - ASSERT(m_element); - if (parserInserted && m_element->document().scriptableDocumentParser() && !m_element->document().isInDocumentWrite()) - m_startLineNumber = m_element->document().scriptableDocumentParser()->textPosition().m_line; + if (parserInserted && m_element.document().scriptableDocumentParser() && !m_element.document().isInDocumentWrite()) + m_startLineNumber = m_element.document().scriptableDocumentParser()->textPosition().m_line; } -ScriptElement::~ScriptElement() +bool ScriptElement::shouldCallFinishedInsertingSubtree(ContainerNode& insertionPoint) { - stopLoadRequest(); + return insertionPoint.isConnected() && !m_parserInserted; } -bool ScriptElement::shouldNotifySubtreeInsertions(ContainerNode& insertionPoint) -{ - return insertionPoint.inDocument() && !m_parserInserted; -} - -void ScriptElement::didNotifySubtreeInsertions(ContainerNode*) +void ScriptElement::finishedInsertingSubtree() { ASSERT(!m_parserInserted); prepareScript(); // FIXME: Provide a real starting line number here. } -void ScriptElement::childrenChanged() +void ScriptElement::childrenChanged(const ContainerNode::ChildChange& childChange) { - if (!m_parserInserted && m_element->inDocument()) + if (!m_parserInserted && childChange.isInsertion() && m_element.isConnected()) prepareScript(); // FIXME: Provide a real starting line number here. } -void ScriptElement::handleSourceAttribute(const String& sourceUrl) +void ScriptElement::handleSourceAttribute(const String& sourceURL) { - if (ignoresLoadRequest() || sourceUrl.isEmpty()) + if (ignoresLoadRequest() || sourceURL.isEmpty()) return; prepareScript(); // FIXME: Provide a real starting line number here. @@ -123,49 +113,65 @@ static bool isLegacySupportedJavaScriptLanguage(const String& language) // We want to accept all the values that either of these browsers accept, but not other values. // FIXME: This function is not HTML5 compliant. These belong in the MIME registry as "text/javascript<version>" entries. - typedef HashSet<String, CaseFoldingHash> LanguageSet; - DEFINE_STATIC_LOCAL(LanguageSet, languages, ()); - if (languages.isEmpty()) { - languages.add("javascript"); - languages.add("javascript"); - languages.add("javascript1.0"); - languages.add("javascript1.1"); - languages.add("javascript1.2"); - languages.add("javascript1.3"); - languages.add("javascript1.4"); - languages.add("javascript1.5"); - languages.add("javascript1.6"); - languages.add("javascript1.7"); - languages.add("livescript"); - languages.add("ecmascript"); - languages.add("jscript"); + typedef HashSet<String, ASCIICaseInsensitiveHash> LanguageSet; + static NeverDestroyed<LanguageSet> languages; + if (languages.get().isEmpty()) { + languages.get().add("javascript"); + languages.get().add("javascript"); + languages.get().add("javascript1.0"); + languages.get().add("javascript1.1"); + languages.get().add("javascript1.2"); + languages.get().add("javascript1.3"); + languages.get().add("javascript1.4"); + languages.get().add("javascript1.5"); + languages.get().add("javascript1.6"); + languages.get().add("javascript1.7"); + languages.get().add("livescript"); + languages.get().add("ecmascript"); + languages.get().add("jscript"); } - return languages.contains(language); + return languages.get().contains(language); } void ScriptElement::dispatchErrorEvent() { - m_element->dispatchEvent(Event::create(eventNames().errorEvent, false, false)); + m_element.dispatchEvent(Event::create(eventNames().errorEvent, false, false)); } -bool ScriptElement::isScriptTypeSupported(LegacyTypeSupport supportLegacyTypes) const +std::optional<ScriptElement::ScriptType> ScriptElement::determineScriptType(LegacyTypeSupport supportLegacyTypes) const { // FIXME: isLegacySupportedJavaScriptLanguage() is not valid HTML5. It is used here to maintain backwards compatibility with existing layout tests. The specific violations are: // - Allowing type=javascript. type= should only support MIME types, such as text/javascript. // - Allowing a different set of languages for language= and type=. language= supports Javascript 1.1 and 1.4-1.6, but type= does not. - String type = typeAttributeValue(); String language = languageAttributeValue(); - if (type.isEmpty() && language.isEmpty()) - return true; // Assume text/javascript. if (type.isEmpty()) { - type = "text/" + language.lower(); - if (MIMETypeRegistry::isSupportedJavaScriptMIMEType(type) || isLegacySupportedJavaScriptLanguage(language)) - return true; - } else if (MIMETypeRegistry::isSupportedJavaScriptMIMEType(type.stripWhiteSpace().lower()) || (supportLegacyTypes == AllowLegacyTypeInTypeAttribute && isLegacySupportedJavaScriptLanguage(type))) - return true; - return false; + if (language.isEmpty()) + return ScriptType::Classic; // Assume text/javascript. + if (MIMETypeRegistry::isSupportedJavaScriptMIMEType("text/" + language)) + return ScriptType::Classic; + if (isLegacySupportedJavaScriptLanguage(language)) + return ScriptType::Classic; + return std::nullopt; + } + if (MIMETypeRegistry::isSupportedJavaScriptMIMEType(type.stripWhiteSpace())) + return ScriptType::Classic; + if (supportLegacyTypes == AllowLegacyTypeInTypeAttribute && isLegacySupportedJavaScriptLanguage(type)) + return ScriptType::Classic; + + // FIXME: XHTML spec defines "defer" attribute. But WebKit does not implement it for a long time. + // And module tag also uses defer attribute semantics. We disable script type="module" for non HTML document. + // Once "defer" is implemented, we can reconsider enabling modules in XHTML. + // https://bugs.webkit.org/show_bug.cgi?id=123387 + if (!m_element.document().isHTMLDocument()) + return std::nullopt; + + // https://html.spec.whatwg.org/multipage/scripting.html#attr-script-type + // Setting the attribute to an ASCII case-insensitive match for the string "module" means that the script is a module script. + if (equalLettersIgnoringASCIICase(type, "module")) + return ScriptType::Module; + return std::nullopt; } // http://dev.w3.org/html5/spec/Overview.html#prepare-a-script @@ -181,18 +187,22 @@ bool ScriptElement::prepareScript(const TextPosition& scriptStartPosition, Legac } else wasParserInserted = false; - if (wasParserInserted && !asyncAttributeValue()) + if (wasParserInserted && !hasAsyncAttribute()) m_forceAsync = true; // FIXME: HTML5 spec says we should check that all children are either comments or empty text nodes. - if (!hasSourceAttribute() && !m_element->firstChild()) + if (!hasSourceAttribute() && !m_element.firstChild()) return false; - if (!m_element->inDocument()) + if (!m_element.isConnected()) return false; - if (!isScriptTypeSupported(supportLegacyTypes)) + ScriptType scriptType = ScriptType::Classic; + if (std::optional<ScriptType> result = determineScriptType(supportLegacyTypes)) + scriptType = result.value(); + else return false; + m_isModuleScript = scriptType == ScriptType::Module; if (wasParserInserted) { m_parserInserted = true; @@ -202,7 +212,7 @@ bool ScriptElement::prepareScript(const TextPosition& scriptStartPosition, Legac m_alreadyStarted = true; // FIXME: If script is parser inserted, verify it's still in the original document. - Document& document = m_element->document(); + Document& document = m_element.document(); // FIXME: Eventually we'd like to evaluate scripts which are inserted into a // viewless document but this'll do for now. @@ -210,180 +220,235 @@ bool ScriptElement::prepareScript(const TextPosition& scriptStartPosition, Legac if (!document.frame()) return false; + if (scriptType == ScriptType::Classic && hasNoModuleAttribute()) + return false; + if (!document.frame()->script().canExecuteScripts(AboutToExecuteScript)) return false; - if (!isScriptForEventSupported()) + if (scriptType == ScriptType::Classic && !isScriptForEventSupported()) return false; + // According to the spec, the module tag ignores the "charset" attribute as the same to the worker's + // importScript. But WebKit supports the "charset" for importScript intentionally. So to be consistent, + // even for the module tags, we handle the "charset" attribute. if (!charsetAttributeValue().isEmpty()) m_characterEncoding = charsetAttributeValue(); else m_characterEncoding = document.charset(); - if (hasSourceAttribute()) - if (!requestScript(sourceAttributeValue())) + if (scriptType == ScriptType::Classic) { + if (hasSourceAttribute()) { + if (!requestClassicScript(sourceAttributeValue())) + return false; + } + } else { + ASSERT(scriptType == ScriptType::Module); + if (!requestModuleScript(scriptStartPosition)) return false; + } + + // All the inlined module script is handled by requestModuleScript. It produces LoadableModuleScript and inlined module script + // is handled as the same to the external module script. - if (hasSourceAttribute() && deferAttributeValue() && m_parserInserted && !asyncAttributeValue()) { + bool isClassicExternalScript = scriptType == ScriptType::Classic && hasSourceAttribute(); + bool isParserInsertedDeferredScript = ((isClassicExternalScript && hasDeferAttribute()) || scriptType == ScriptType::Module) + && m_parserInserted && !hasAsyncAttribute(); + if (isParserInsertedDeferredScript) { m_willExecuteWhenDocumentFinishedParsing = true; m_willBeParserExecuted = true; - } else if (hasSourceAttribute() && m_parserInserted && !asyncAttributeValue()) + } else if (isClassicExternalScript && m_parserInserted && !hasAsyncAttribute()) { + ASSERT(scriptType == ScriptType::Classic); m_willBeParserExecuted = true; - else if (!hasSourceAttribute() && m_parserInserted && !document.haveStylesheetsLoaded()) { + } else if ((isClassicExternalScript || scriptType == ScriptType::Module) && !hasAsyncAttribute() && !m_forceAsync) { + m_willExecuteInOrder = true; + ASSERT(m_loadableScript); + document.scriptRunner()->queueScriptForExecution(*this, *m_loadableScript, ScriptRunner::IN_ORDER_EXECUTION); + } else if (hasSourceAttribute() || scriptType == ScriptType::Module) { + ASSERT(m_loadableScript); + ASSERT(hasAsyncAttribute() || m_forceAsync); + document.scriptRunner()->queueScriptForExecution(*this, *m_loadableScript, ScriptRunner::ASYNC_EXECUTION); + } else if (!hasSourceAttribute() && m_parserInserted && !document.haveStylesheetsLoaded()) { + ASSERT(scriptType == ScriptType::Classic); m_willBeParserExecuted = true; m_readyToBeParserExecuted = true; - } else if (hasSourceAttribute() && !asyncAttributeValue() && !m_forceAsync) { - m_willExecuteInOrder = true; - document.scriptRunner()->queueScriptForExecution(this, m_cachedScript, ScriptRunner::IN_ORDER_EXECUTION); - m_cachedScript->addClient(this); - } else if (hasSourceAttribute()) { - m_element->document().scriptRunner()->queueScriptForExecution(this, m_cachedScript, ScriptRunner::ASYNC_EXECUTION); - m_cachedScript->addClient(this); } else { - // Reset line numbering for nested writes. + ASSERT(scriptType == ScriptType::Classic); TextPosition position = document.isInDocumentWrite() ? TextPosition() : scriptStartPosition; - executeScript(ScriptSourceCode(scriptContent(), document.url(), position)); + executeClassicScript(ScriptSourceCode(scriptContent(), document.url(), position, JSC::SourceProviderSourceType::Program, InlineClassicScript::create(*this))); } return true; } -bool ScriptElement::requestScript(const String& sourceUrl) +bool ScriptElement::requestClassicScript(const String& sourceURL) { - Ref<Document> originalDocument(m_element->document()); - if (!m_element->dispatchBeforeLoadEvent(sourceUrl)) - return false; - if (!m_element->inDocument() || &m_element->document() != &originalDocument.get()) + Ref<Document> originalDocument(m_element.document()); + if (!m_element.dispatchBeforeLoadEvent(sourceURL)) return false; - if (!m_element->document().contentSecurityPolicy()->allowScriptNonce(m_element->fastGetAttribute(HTMLNames::nonceAttr), m_element->document().url(), m_startLineNumber, m_element->document().completeURL(sourceUrl))) + bool didEventListenerDisconnectThisElement = !m_element.isConnected() || &m_element.document() != originalDocument.ptr(); + if (didEventListenerDisconnectThisElement) return false; - ASSERT(!m_cachedScript); - if (!stripLeadingAndTrailingHTMLSpaces(sourceUrl).isEmpty()) { - CachedResourceRequest request(ResourceRequest(m_element->document().completeURL(sourceUrl))); + ASSERT(!m_loadableScript); + if (!stripLeadingAndTrailingHTMLSpaces(sourceURL).isEmpty()) { + auto script = LoadableClassicScript::create( + m_element.attributeWithoutSynchronization(HTMLNames::nonceAttr), + m_element.attributeWithoutSynchronization(HTMLNames::crossoriginAttr), + scriptCharset(), + m_element.localName(), + m_element.isInUserAgentShadowTree()); + if (script->load(m_element.document(), m_element.document().completeURL(sourceURL))) { + m_loadableScript = WTFMove(script); + m_isExternalScript = true; + } + } + + if (m_loadableScript) + return true; - String crossOriginMode = m_element->fastGetAttribute(HTMLNames::crossoriginAttr); - if (!crossOriginMode.isNull()) { - m_requestUsesAccessControl = true; - StoredCredentials allowCredentials = equalIgnoringCase(crossOriginMode, "use-credentials") ? AllowStoredCredentials : DoNotAllowStoredCredentials; - updateRequestForAccessControl(request.mutableResourceRequest(), m_element->document().securityOrigin(), allowCredentials); + callOnMainThread([this, element = Ref<Element>(m_element)] { + dispatchErrorEvent(); + }); + return false; +} + +bool ScriptElement::requestModuleScript(const TextPosition& scriptStartPosition) +{ + String nonce = m_element.attributeWithoutSynchronization(HTMLNames::nonceAttr); + String crossOriginMode = m_element.attributeWithoutSynchronization(HTMLNames::crossoriginAttr); + if (crossOriginMode.isNull()) + crossOriginMode = ASCIILiteral("omit"); + + if (hasSourceAttribute()) { + String sourceURL = sourceAttributeValue(); + Ref<Document> originalDocument(m_element.document()); + if (!m_element.dispatchBeforeLoadEvent(sourceURL)) + return false; + + bool didEventListenerDisconnectThisElement = !m_element.isConnected() || &m_element.document() != originalDocument.ptr(); + if (didEventListenerDisconnectThisElement) + return false; + + if (stripLeadingAndTrailingHTMLSpaces(sourceURL).isEmpty()) { + dispatchErrorEvent(); + return false; } - request.setCharset(scriptCharset()); - request.setInitiator(element()); - m_cachedScript = m_element->document().cachedResourceLoader()->requestScript(request); - m_isExternalScript = true; - } + auto moduleScriptRootURL = m_element.document().completeURL(sourceURL); + if (!moduleScriptRootURL.isValid()) { + dispatchErrorEvent(); + return false; + } - if (m_cachedScript) { + m_isExternalScript = true; + auto script = LoadableModuleScript::create(nonce, crossOriginMode, scriptCharset(), m_element.localName(), m_element.isInUserAgentShadowTree()); + script->load(m_element.document(), moduleScriptRootURL); + m_loadableScript = WTFMove(script); return true; } - dispatchErrorEvent(); - return false; + auto script = LoadableModuleScript::create(nonce, crossOriginMode, scriptCharset(), m_element.localName(), m_element.isInUserAgentShadowTree()); + + TextPosition position = m_element.document().isInDocumentWrite() ? TextPosition() : scriptStartPosition; + ScriptSourceCode sourceCode(scriptContent(), m_element.document().url(), position, JSC::SourceProviderSourceType::Module, script.copyRef()); + + ASSERT(m_element.document().contentSecurityPolicy()); + const auto& contentSecurityPolicy = *m_element.document().contentSecurityPolicy(); + bool hasKnownNonce = contentSecurityPolicy.allowScriptWithNonce(nonce, m_element.isInUserAgentShadowTree()); + if (!contentSecurityPolicy.allowInlineScript(m_element.document().url(), m_startLineNumber, sourceCode.source().toStringWithoutCopying(), hasKnownNonce)) + return false; + + script->load(m_element.document(), sourceCode); + m_loadableScript = WTFMove(script); + return true; } -void ScriptElement::executeScript(const ScriptSourceCode& sourceCode) +void ScriptElement::executeClassicScript(const ScriptSourceCode& sourceCode) { + ASSERT_WITH_SECURITY_IMPLICATION(NoEventDispatchAssertion::isEventAllowedInMainThread()); ASSERT(m_alreadyStarted); if (sourceCode.isEmpty()) return; - if (!m_element->document().contentSecurityPolicy()->allowScriptNonce(m_element->fastGetAttribute(HTMLNames::nonceAttr), m_element->document().url(), m_startLineNumber)) - return; + if (!m_isExternalScript) { + ASSERT(m_element.document().contentSecurityPolicy()); + const ContentSecurityPolicy& contentSecurityPolicy = *m_element.document().contentSecurityPolicy(); + bool hasKnownNonce = contentSecurityPolicy.allowScriptWithNonce(m_element.attributeWithoutSynchronization(HTMLNames::nonceAttr), m_element.isInUserAgentShadowTree()); + if (!contentSecurityPolicy.allowInlineScript(m_element.document().url(), m_startLineNumber, sourceCode.source().toStringWithoutCopying(), hasKnownNonce)) + return; + } - if (!m_isExternalScript && !m_element->document().contentSecurityPolicy()->allowInlineScript(m_element->document().url(), m_startLineNumber)) + auto& document = m_element.document(); + auto* frame = document.frame(); + if (!frame) return; -#if ENABLE(NOSNIFF) - if (m_isExternalScript && m_cachedScript && !m_cachedScript->mimeTypeAllowedByNosniff()) { - m_element->document().addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, "Refused to execute script from '" + m_cachedScript->url().stringCenterEllipsizedToLength() + "' because its MIME type ('" + m_cachedScript->mimeType() + "') is not executable, and strict MIME type checking is enabled."); - return; - } -#endif + IgnoreDestructiveWriteCountIncrementer ignoreDesctructiveWriteCountIncrementer(m_isExternalScript ? &document : nullptr); + CurrentScriptIncrementer currentScriptIncrementer(document, m_element); - Ref<Document> document(m_element->document()); - if (Frame* frame = document->frame()) { - IgnoreDestructiveWriteCountIncrementer ignoreDesctructiveWriteCountIncrementer(m_isExternalScript ? &document.get() : 0); - CurrentScriptIncrementer currentScriptIncrementer(&document.get(), m_element); - - // Create a script from the script element node, using the script - // block's source and the script block's type. - // Note: This is where the script is compiled and actually executed. - frame->script().evaluate(sourceCode); - } + frame->script().evaluate(sourceCode); } -void ScriptElement::stopLoadRequest() +void ScriptElement::executeModuleScript(LoadableModuleScript& loadableModuleScript) { - if (m_cachedScript) { - if (!m_willBeParserExecuted) - m_cachedScript->removeClient(this); - m_cachedScript = 0; - } + // https://html.spec.whatwg.org/multipage/scripting.html#execute-the-script-block + + ASSERT(!loadableModuleScript.error()); + + auto& document = m_element.document(); + auto* frame = document.frame(); + if (!frame) + return; + + IgnoreDestructiveWriteCountIncrementer ignoreDesctructiveWriteCountIncrementer(&document); + CurrentScriptIncrementer currentScriptIncrementer(document, m_element); + + frame->script().linkAndEvaluateModuleScript(loadableModuleScript); } -void ScriptElement::execute(CachedScript* cachedScript) +void ScriptElement::executeScriptAndDispatchEvent(LoadableScript& loadableScript) { - ASSERT(!m_willBeParserExecuted); - ASSERT(cachedScript); - if (cachedScript->errorOccurred()) + if (std::optional<LoadableScript::Error> error = loadableScript.error()) { + if (std::optional<LoadableScript::ConsoleMessage> message = error->consoleMessage) + m_element.document().addConsoleMessage(message->source, message->level, message->message); dispatchErrorEvent(); - else if (!cachedScript->wasCanceled()) { - executeScript(ScriptSourceCode(cachedScript)); + } else if (!loadableScript.wasCanceled()) { + ASSERT(!loadableScript.error()); + loadableScript.execute(*this); dispatchLoadEvent(); } - cachedScript->removeClient(this); } -void ScriptElement::notifyFinished(CachedResource* resource) +void ScriptElement::executePendingScript(PendingScript& pendingScript) { - ASSERT(!m_willBeParserExecuted); - - // CachedResource possibly invokes this notifyFinished() more than - // once because ScriptElement doesn't unsubscribe itself from - // CachedResource here and does it in execute() instead. - // We use m_cachedScript to check if this function is already called. - ASSERT_UNUSED(resource, resource == m_cachedScript); - if (!m_cachedScript) - return; - - if (m_requestUsesAccessControl - && !m_element->document().securityOrigin()->canRequest(m_cachedScript->response().url()) - && !m_cachedScript->passesAccessControlCheck(m_element->document().securityOrigin())) { - - dispatchErrorEvent(); - DEFINE_STATIC_LOCAL(String, consoleMessage, (ASCIILiteral("Cross-origin script load denied by Cross-Origin Resource Sharing policy."))); - m_element->document().addConsoleMessage(JSMessageSource, ErrorMessageLevel, consoleMessage); - return; + if (auto* loadableScript = pendingScript.loadableScript()) + executeScriptAndDispatchEvent(*loadableScript); + else { + ASSERT(!pendingScript.error()); + ASSERT_WITH_MESSAGE(scriptType() == ScriptType::Classic, "Module script always have a loadableScript pointer."); + executeClassicScript(ScriptSourceCode(scriptContent(), m_element.document().url(), pendingScript.startingPosition(), JSC::SourceProviderSourceType::Program, InlineClassicScript::create(*this))); + dispatchLoadEvent(); } - - if (m_willExecuteInOrder) - m_element->document().scriptRunner()->notifyScriptReady(this, ScriptRunner::IN_ORDER_EXECUTION); - else - m_element->document().scriptRunner()->notifyScriptReady(this, ScriptRunner::ASYNC_EXECUTION); - - m_cachedScript = 0; } bool ScriptElement::ignoresLoadRequest() const { - return m_alreadyStarted || m_isExternalScript || m_parserInserted || !m_element->inDocument(); + return m_alreadyStarted || m_isExternalScript || m_parserInserted || !m_element.isConnected(); } bool ScriptElement::isScriptForEventSupported() const { String eventAttribute = eventAttributeValue(); String forAttribute = forAttributeValue(); - if (!eventAttribute.isEmpty() && !forAttribute.isEmpty()) { - forAttribute = forAttribute.stripWhiteSpace(); - if (!equalIgnoringCase(forAttribute, "window")) + if (!eventAttribute.isNull() && !forAttribute.isNull()) { + forAttribute = stripLeadingAndTrailingHTMLSpaces(forAttribute); + if (!equalLettersIgnoringASCIICase(forAttribute, "window")) return false; - eventAttribute = eventAttribute.stripWhiteSpace(); - if (!equalIgnoringCase(eventAttribute, "onload") && !equalIgnoringCase(eventAttribute, "onload()")) + eventAttribute = stripLeadingAndTrailingHTMLSpaces(eventAttribute); + if (!equalLettersIgnoringASCIICase(eventAttribute, "onload") && !equalLettersIgnoringASCIICase(eventAttribute, "onload()")) return false; } return true; @@ -391,20 +456,32 @@ bool ScriptElement::isScriptForEventSupported() const String ScriptElement::scriptContent() const { - return TextNodeTraversal::contentsAsString(m_element); + StringBuilder result; + for (auto* text = TextNodeTraversal::firstChild(m_element); text; text = TextNodeTraversal::nextSibling(*text)) + result.append(text->data()); + return result.toString(); } -ScriptElement* toScriptElementIfPossible(Element* element) +void ScriptElement::ref() { - if (isHTMLScriptElement(element)) - return toHTMLScriptElement(element); + m_element.ref(); +} -#if ENABLE(SVG) - if (isSVGScriptElement(element)) - return toSVGScriptElement(element); -#endif +void ScriptElement::deref() +{ + m_element.deref(); +} - return 0; +bool isScriptElement(Element& element) +{ + return is<HTMLScriptElement>(element) || is<SVGScriptElement>(element); +} + +ScriptElement& downcastScriptElement(Element& element) +{ + if (is<HTMLScriptElement>(element)) + return downcast<HTMLScriptElement>(element); + return downcast<SVGScriptElement>(element); } } diff --git a/Source/WebCore/dom/ScriptElement.h b/Source/WebCore/dom/ScriptElement.h index e1b5fda88..e5e95fd7a 100644 --- a/Source/WebCore/dom/ScriptElement.h +++ b/Source/WebCore/dom/ScriptElement.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2008 Nikolas Zimmermann <zimmermann@kde.org> + * Copyright (C) 2009-2017 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -18,70 +19,85 @@ * */ -#ifndef ScriptElement_h -#define ScriptElement_h +#pragma once #include "CachedResourceClient.h" #include "CachedResourceHandle.h" +#include "ContainerNode.h" +#include "LoadableScript.h" +#include "LoadableScriptClient.h" +#include "Timer.h" #include <wtf/text/TextPosition.h> -#include <wtf/text/WTFString.h> namespace WebCore { class CachedScript; class ContainerNode; class Element; -class ScriptElement; +class LoadableModuleScript; +class PendingScript; class ScriptSourceCode; +class URL; -class ScriptElement : private CachedResourceClient { - WTF_MAKE_FAST_ALLOCATED; +class ScriptElement { public: - ScriptElement(Element*, bool createdByParser, bool isEvaluated); - virtual ~ScriptElement(); + virtual ~ScriptElement() { } - Element* element() const { return m_element; } + Element& element() { return m_element; } + const Element& element() const { return m_element; } enum LegacyTypeSupport { DisallowLegacyTypeInTypeAttribute, AllowLegacyTypeInTypeAttribute }; - bool prepareScript(const TextPosition& scriptStartPosition = TextPosition::minimumPosition(), LegacyTypeSupport = DisallowLegacyTypeInTypeAttribute); + bool prepareScript(const TextPosition& scriptStartPosition = TextPosition(), LegacyTypeSupport = DisallowLegacyTypeInTypeAttribute); String scriptCharset() const { return m_characterEncoding; } - String scriptContent() const; - void executeScript(const ScriptSourceCode&); - void execute(CachedScript*); + WEBCORE_EXPORT String scriptContent() const; + void executeClassicScript(const ScriptSourceCode&); + void executeModuleScript(LoadableModuleScript&); + + void executePendingScript(PendingScript&); // XML parser calls these virtual void dispatchLoadEvent() = 0; void dispatchErrorEvent(); - bool isScriptTypeSupported(LegacyTypeSupport) const; bool haveFiredLoadEvent() const { return m_haveFiredLoad; } bool willBeParserExecuted() const { return m_willBeParserExecuted; } bool readyToBeParserExecuted() const { return m_readyToBeParserExecuted; } bool willExecuteWhenDocumentFinishedParsing() const { return m_willExecuteWhenDocumentFinishedParsing; } - CachedResourceHandle<CachedScript> cachedScript() { return m_cachedScript; } + bool willExecuteInOrder() const { return m_willExecuteInOrder; } + LoadableScript* loadableScript() { return m_loadableScript.get(); } + + // https://html.spec.whatwg.org/multipage/scripting.html#concept-script-type + enum class ScriptType { Classic, Module }; + ScriptType scriptType() const { return m_isModuleScript ? ScriptType::Module : ScriptType::Classic; } + + void ref(); + void deref(); protected: + ScriptElement(Element&, bool createdByParser, bool isEvaluated); + void setHaveFiredLoadEvent(bool haveFiredLoad) { m_haveFiredLoad = haveFiredLoad; } bool isParserInserted() const { return m_parserInserted; } bool alreadyStarted() const { return m_alreadyStarted; } bool forceAsync() const { return m_forceAsync; } // Helper functions used by our parent classes. - bool shouldNotifySubtreeInsertions(ContainerNode&); - void didNotifySubtreeInsertions(ContainerNode*); - void childrenChanged(); - void handleSourceAttribute(const String& sourceUrl); + bool shouldCallFinishedInsertingSubtree(ContainerNode&); + void finishedInsertingSubtree(); + void childrenChanged(const ContainerNode::ChildChange&); + void handleSourceAttribute(const String& sourceURL); void handleAsyncAttribute(); private: + void executeScriptAndDispatchEvent(LoadableScript&); + + std::optional<ScriptType> determineScriptType(LegacyTypeSupport) const; bool ignoresLoadRequest() const; bool isScriptForEventSupported() const; - bool requestScript(const String& sourceUrl); - void stopLoadRequest(); - - virtual void notifyFinished(CachedResource*) override; + bool requestClassicScript(const String& sourceURL); + bool requestModuleScript(const TextPosition& scriptStartPosition); virtual String sourceAttributeValue() const = 0; virtual String charsetAttributeValue() const = 0; @@ -89,12 +105,12 @@ private: virtual String languageAttributeValue() const = 0; virtual String forAttributeValue() const = 0; virtual String eventAttributeValue() const = 0; - virtual bool asyncAttributeValue() const = 0; - virtual bool deferAttributeValue() const = 0; + virtual bool hasAsyncAttribute() const = 0; + virtual bool hasDeferAttribute() const = 0; virtual bool hasSourceAttribute() const = 0; + virtual bool hasNoModuleAttribute() const = 0; - Element* m_element; - CachedResourceHandle<CachedScript> m_cachedScript; + Element& m_element; WTF::OrdinalNumber m_startLineNumber; bool m_parserInserted : 1; bool m_isExternalScript : 1; @@ -105,13 +121,14 @@ private: bool m_willExecuteWhenDocumentFinishedParsing : 1; bool m_forceAsync : 1; bool m_willExecuteInOrder : 1; - bool m_requestUsesAccessControl : 1; + bool m_isModuleScript : 1; String m_characterEncoding; String m_fallbackCharacterEncoding; + RefPtr<LoadableScript> m_loadableScript; }; -ScriptElement* toScriptElementIfPossible(Element*); +// FIXME: replace with is/downcast<ScriptElement>. +bool isScriptElement(Element&); +ScriptElement& downcastScriptElement(Element&); } - -#endif diff --git a/Source/WebCore/dom/ScriptElementCachedScriptFetcher.cpp b/Source/WebCore/dom/ScriptElementCachedScriptFetcher.cpp new file mode 100644 index 000000000..bd49f90c2 --- /dev/null +++ b/Source/WebCore/dom/ScriptElementCachedScriptFetcher.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2017 Yusuke Suzuki <utatane.tea@gmail.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ScriptElementCachedScriptFetcher.h" + +#include "Element.h" +#include "ScriptElement.h" + +namespace WebCore { + +CachedResourceHandle<CachedScript> ScriptElementCachedScriptFetcher::requestModuleScript(Document& document, const URL& sourceURL) const +{ + // https://github.com/tc39/proposal-dynamic-import/blob/master/HTML Integration.md + // If the fetcher is not module script, credential mode is always "omit". + + return requestScriptWithCache(document, sourceURL, isClassicScript() ? ASCIILiteral("omit") : m_crossOriginMode); +} + +} diff --git a/Source/WebCore/dom/ScriptElementCachedScriptFetcher.h b/Source/WebCore/dom/ScriptElementCachedScriptFetcher.h new file mode 100644 index 000000000..410cd306a --- /dev/null +++ b/Source/WebCore/dom/ScriptElementCachedScriptFetcher.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2017 Yusuke Suzuki <utatane.tea@gmail.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "CachedScriptFetcher.h" + +namespace WebCore { + +class ScriptElementCachedScriptFetcher : public CachedScriptFetcher { +public: + virtual CachedResourceHandle<CachedScript> requestModuleScript(Document&, const URL& sourceURL) const; + + virtual bool isClassicScript() const = 0; + virtual bool isModuleScript() const = 0; + + const String& crossOriginMode() const { return m_crossOriginMode; } + +protected: + ScriptElementCachedScriptFetcher(const String& nonce, const String& crossOriginMode, const String& charset, const AtomicString& initiatorName, bool isInUserAgentShadowTree) + : CachedScriptFetcher(nonce, charset, initiatorName, isInUserAgentShadowTree) + , m_crossOriginMode(crossOriginMode) + { + } + +private: + String m_crossOriginMode; +}; + +} // namespace WebCore diff --git a/Source/WebCore/dom/ScriptExecutionContext.cpp b/Source/WebCore/dom/ScriptExecutionContext.cpp index ca6041f9a..328818f4a 100644 --- a/Source/WebCore/dom/ScriptExecutionContext.cpp +++ b/Source/WebCore/dom/ScriptExecutionContext.cpp @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -29,52 +29,39 @@ #include "ScriptExecutionContext.h" #include "CachedScript.h" +#include "CommonVM.h" #include "DOMTimer.h" +#include "DatabaseContext.h" +#include "Document.h" #include "ErrorEvent.h" +#include "JSDOMWindow.h" #include "MessagePort.h" +#include "NoEventDispatchAssertion.h" #include "PublicURLManager.h" -#include "ScriptCallStack.h" +#include "ResourceRequest.h" +#include "ScriptState.h" #include "Settings.h" #include "WorkerGlobalScope.h" #include "WorkerThread.h" +#include <heap/StrongInlines.h> +#include <inspector/ScriptCallStack.h> +#include <runtime/Exception.h> #include <wtf/MainThread.h> #include <wtf/Ref.h> -// FIXME: This is a layering violation. -#include "JSDOMWindow.h" - -#if PLATFORM(IOS) -#include "Document.h" -#endif - -#if ENABLE(SQL_DATABASE) -#include "DatabaseContext.h" -#endif +using namespace Inspector; namespace WebCore { -class ProcessMessagesSoonTask : public ScriptExecutionContext::Task { -public: - static PassOwnPtr<ProcessMessagesSoonTask> create() - { - return adoptPtr(new ProcessMessagesSoonTask); - } - - virtual void performTask(ScriptExecutionContext* context) override - { - context->dispatchMessagePortEvents(); - } -}; - -class ScriptExecutionContext::PendingException { - WTF_MAKE_NONCOPYABLE(PendingException); +struct ScriptExecutionContext::PendingException { + WTF_MAKE_FAST_ALLOCATED; public: - PendingException(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, PassRefPtr<ScriptCallStack> callStack) + PendingException(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, RefPtr<ScriptCallStack>&& callStack) : m_errorMessage(errorMessage) , m_lineNumber(lineNumber) , m_columnNumber(columnNumber) , m_sourceURL(sourceURL) - , m_callStack(callStack) + , m_callStack(WTFMove(callStack)) { } String m_errorMessage; @@ -84,240 +71,312 @@ public: RefPtr<ScriptCallStack> m_callStack; }; -void ScriptExecutionContext::AddConsoleMessageTask::performTask(ScriptExecutionContext* context) +ScriptExecutionContext::ScriptExecutionContext() { - context->addConsoleMessage(m_source, m_level, m_message); } -ScriptExecutionContext::ScriptExecutionContext() - : m_iteratingActiveDOMObjects(false) - , m_inDestructor(false) - , m_circularSequentialID(0) - , m_inDispatchErrorEvent(false) - , m_activeDOMObjectsAreSuspended(false) - , m_reasonForSuspendingActiveDOMObjects(static_cast<ActiveDOMObject::ReasonForSuspension>(-1)) - , m_activeDOMObjectsAreStopped(false) +#if ASSERT_DISABLED + +inline void ScriptExecutionContext::checkConsistency() const { } -ScriptExecutionContext::~ScriptExecutionContext() +#else + +void ScriptExecutionContext::checkConsistency() const { - m_inDestructor = true; - for (HashSet<ContextDestructionObserver*>::iterator iter = m_destructionObservers.begin(); iter != m_destructionObservers.end(); iter = m_destructionObservers.begin()) { - ContextDestructionObserver* observer = *iter; - m_destructionObservers.remove(observer); - ASSERT(observer->scriptExecutionContext() == this); - observer->contextDestroyed(); - } + for (auto* messagePort : m_messagePorts) + ASSERT(messagePort->scriptExecutionContext() == this); + + for (auto* destructionObserver : m_destructionObservers) + ASSERT(destructionObserver->scriptExecutionContext() == this); - HashSet<MessagePort*>::iterator messagePortsEnd = m_messagePorts.end(); - for (HashSet<MessagePort*>::iterator iter = m_messagePorts.begin(); iter != messagePortsEnd; ++iter) { - ASSERT((*iter)->scriptExecutionContext() == this); - (*iter)->contextDestroyed(); + for (auto* activeDOMObject : m_activeDOMObjects) { + ASSERT(activeDOMObject->scriptExecutionContext() == this); + activeDOMObject->assertSuspendIfNeededWasCalled(); } -#if ENABLE(BLOB) - if (m_publicURLManager) - m_publicURLManager->contextDestroyed(); +} + +#endif + +ScriptExecutionContext::~ScriptExecutionContext() +{ + checkConsistency(); + +#if !ASSERT_DISABLED + m_inScriptExecutionContextDestructor = true; +#endif + + while (auto* destructionObserver = m_destructionObservers.takeAny()) + destructionObserver->contextDestroyed(); + + for (auto* messagePort : m_messagePorts) + messagePort->contextDestroyed(); + +#if !ASSERT_DISABLED + m_inScriptExecutionContextDestructor = false; #endif } void ScriptExecutionContext::processMessagePortMessagesSoon() { - postTask(ProcessMessagesSoonTask::create()); + if (m_willProcessMessagePortMessagesSoon) + return; + + m_willProcessMessagePortMessagesSoon = true; + postTask([] (ScriptExecutionContext& context) { + context.dispatchMessagePortEvents(); + }); } void ScriptExecutionContext::dispatchMessagePortEvents() { - Ref<ScriptExecutionContext> protect(*this); - - // Make a frozen copy. - Vector<MessagePort*> ports; - copyToVector(m_messagePorts, ports); - - unsigned portCount = ports.size(); - for (unsigned i = 0; i < portCount; ++i) { - MessagePort* port = ports[i]; - // The port may be destroyed, and another one created at the same address, but this is safe, as the worst that can happen - // as a result is that dispatchMessages() will be called needlessly. - if (m_messagePorts.contains(port) && port->started()) - port->dispatchMessages(); + checkConsistency(); + + Ref<ScriptExecutionContext> protectedThis(*this); + ASSERT(m_willProcessMessagePortMessagesSoon); + m_willProcessMessagePortMessagesSoon = false; + + // Make a frozen copy of the ports so we can iterate while new ones might be added or destroyed. + Vector<MessagePort*> possibleMessagePorts; + copyToVector(m_messagePorts, possibleMessagePorts); + for (auto* messagePort : possibleMessagePorts) { + // The port may be destroyed, and another one created at the same address, + // but this is harmless. The worst that can happen as a result is that + // dispatchMessages() will be called needlessly. + if (m_messagePorts.contains(messagePort) && messagePort->started()) + messagePort->dispatchMessages(); } } -void ScriptExecutionContext::createdMessagePort(MessagePort* port) +void ScriptExecutionContext::createdMessagePort(MessagePort& messagePort) { - ASSERT(port); - ASSERT((isDocument() && isMainThread()) - || (isWorkerGlobalScope() && currentThread() == static_cast<WorkerGlobalScope*>(this)->thread()->threadID())); + ASSERT((is<Document>(*this) && isMainThread()) + || (is<WorkerGlobalScope>(*this) && currentThread() == downcast<WorkerGlobalScope>(*this).thread().threadID())); - m_messagePorts.add(port); + m_messagePorts.add(&messagePort); } -void ScriptExecutionContext::destroyedMessagePort(MessagePort* port) +void ScriptExecutionContext::destroyedMessagePort(MessagePort& messagePort) { - ASSERT(port); - ASSERT((isDocument() && isMainThread()) - || (isWorkerGlobalScope() && currentThread() == static_cast<WorkerGlobalScope*>(this)->thread()->threadID())); + ASSERT((is<Document>(*this) && isMainThread()) + || (is<WorkerGlobalScope>(*this) && currentThread() == downcast<WorkerGlobalScope>(*this).thread().threadID())); - m_messagePorts.remove(port); + m_messagePorts.remove(&messagePort); } -bool ScriptExecutionContext::canSuspendActiveDOMObjects() +void ScriptExecutionContext::didLoadResourceSynchronously() { - // No protection against m_activeDOMObjects changing during iteration: canSuspend() shouldn't execute arbitrary JS. - m_iteratingActiveDOMObjects = true; - ActiveDOMObjectsSet::iterator activeObjectsEnd = m_activeDOMObjects.end(); - for (ActiveDOMObjectsSet::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) { - ASSERT((*iter)->scriptExecutionContext() == this); - ASSERT((*iter)->suspendIfNeededCalled()); - if (!(*iter)->canSuspend()) { - m_iteratingActiveDOMObjects = false; - return false; +} + +bool ScriptExecutionContext::canSuspendActiveDOMObjectsForDocumentSuspension(Vector<ActiveDOMObject*>* unsuspendableObjects) +{ + checkConsistency(); + + bool canSuspend = true; + + m_activeDOMObjectAdditionForbidden = true; +#if !ASSERT_DISABLED || ENABLE(SECURITY_ASSERTIONS) + m_activeDOMObjectRemovalForbidden = true; +#endif + + // We assume that m_activeDOMObjects will not change during iteration: canSuspend + // functions should not add new active DOM objects, nor execute arbitrary JavaScript. + // An ASSERT_WITH_SECURITY_IMPLICATION or RELEASE_ASSERT will fire if this happens, but it's important to code + // canSuspend functions so it will not happen! + NoEventDispatchAssertion assertNoEventDispatch; + for (auto* activeDOMObject : m_activeDOMObjects) { + if (!activeDOMObject->canSuspendForDocumentSuspension()) { + canSuspend = false; + if (unsuspendableObjects) + unsuspendableObjects->append(activeDOMObject); + else + break; } } - m_iteratingActiveDOMObjects = false; - return true; + + m_activeDOMObjectAdditionForbidden = false; +#if !ASSERT_DISABLED || ENABLE(SECURITY_ASSERTIONS) + m_activeDOMObjectRemovalForbidden = false; +#endif + + return canSuspend; } void ScriptExecutionContext::suspendActiveDOMObjects(ActiveDOMObject::ReasonForSuspension why) { -#if PLATFORM(IOS) + checkConsistency(); + if (m_activeDOMObjectsAreSuspended) { - ASSERT(m_reasonForSuspendingActiveDOMObjects == ActiveDOMObject::DocumentWillBePaused); + // A page may subsequently suspend DOM objects, say as part of entering the page cache, 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(m_reasonForSuspendingActiveDOMObjects == ActiveDOMObject::PageWillBeSuspended); return; } -#endif - // No protection against m_activeDOMObjects changing during iteration: suspend() shouldn't execute arbitrary JS. - m_iteratingActiveDOMObjects = true; - ActiveDOMObjectsSet::iterator activeObjectsEnd = m_activeDOMObjects.end(); - for (ActiveDOMObjectsSet::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) { - ASSERT((*iter)->scriptExecutionContext() == this); - ASSERT((*iter)->suspendIfNeededCalled()); - (*iter)->suspend(why); - } - m_iteratingActiveDOMObjects = false; m_activeDOMObjectsAreSuspended = true; + + m_activeDOMObjectAdditionForbidden = true; +#if !ASSERT_DISABLED || ENABLE(SECURITY_ASSERTIONS) + m_activeDOMObjectRemovalForbidden = true; +#endif + + // We assume that m_activeDOMObjects will not change during iteration: suspend + // functions should not add new active DOM objects, nor execute arbitrary JavaScript. + // An ASSERT_WITH_SECURITY_IMPLICATION or RELEASE_ASSERT will fire if this happens, but it's important to code + // suspend functions so it will not happen! + NoEventDispatchAssertion assertNoEventDispatch; + for (auto* activeDOMObject : m_activeDOMObjects) + activeDOMObject->suspend(why); + + m_activeDOMObjectAdditionForbidden = false; +#if !ASSERT_DISABLED || ENABLE(SECURITY_ASSERTIONS) + m_activeDOMObjectRemovalForbidden = false; +#endif + m_reasonForSuspendingActiveDOMObjects = why; } void ScriptExecutionContext::resumeActiveDOMObjects(ActiveDOMObject::ReasonForSuspension why) { + checkConsistency(); + if (m_reasonForSuspendingActiveDOMObjects != why) return; - m_activeDOMObjectsAreSuspended = false; - // No protection against m_activeDOMObjects changing during iteration: resume() shouldn't execute arbitrary JS. - m_iteratingActiveDOMObjects = true; - ActiveDOMObjectsSet::iterator activeObjectsEnd = m_activeDOMObjects.end(); - for (ActiveDOMObjectsSet::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) { - ASSERT((*iter)->scriptExecutionContext() == this); - ASSERT((*iter)->suspendIfNeededCalled()); - (*iter)->resume(); - } - m_iteratingActiveDOMObjects = false; + + m_activeDOMObjectAdditionForbidden = true; +#if !ASSERT_DISABLED || ENABLE(SECURITY_ASSERTIONS) + m_activeDOMObjectRemovalForbidden = true; +#endif + + // We assume that m_activeDOMObjects will not change during iteration: resume + // functions should not add new active DOM objects, nor execute arbitrary JavaScript. + // An ASSERT_WITH_SECURITY_IMPLICATION or RELEASE_ASSERT will fire if this happens, but it's important to code + // resume functions so it will not happen! + NoEventDispatchAssertion assertNoEventDispatch; + for (auto* activeDOMObject : m_activeDOMObjects) + activeDOMObject->resume(); + + m_activeDOMObjectAdditionForbidden = false; +#if !ASSERT_DISABLED || ENABLE(SECURITY_ASSERTIONS) + m_activeDOMObjectRemovalForbidden = false; +#endif } void ScriptExecutionContext::stopActiveDOMObjects() { + checkConsistency(); + if (m_activeDOMObjectsAreStopped) return; m_activeDOMObjectsAreStopped = true; - // No protection against m_activeDOMObjects changing during iteration: stop() shouldn't execute arbitrary JS. - m_iteratingActiveDOMObjects = true; - ActiveDOMObjectsSet::iterator activeObjectsEnd = m_activeDOMObjects.end(); - for (ActiveDOMObjectsSet::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) { - ASSERT((*iter)->scriptExecutionContext() == this); - ASSERT((*iter)->suspendIfNeededCalled()); - (*iter)->stop(); + + // Make a frozen copy of the objects so we can iterate while new ones might be destroyed. + Vector<ActiveDOMObject*> possibleActiveDOMObjects; + copyToVector(m_activeDOMObjects, possibleActiveDOMObjects); + + m_activeDOMObjectAdditionForbidden = true; + + // We assume that new objects will not be added to m_activeDOMObjects during iteration: + // stop functions should not add new active DOM objects, nor execute arbitrary JavaScript. + // An ASSERT_WITH_SECURITY_IMPLICATION or RELEASE_ASSERT will fire if this happens, but it's important to code stop functions + // so it will not happen! + NoEventDispatchAssertion assertNoEventDispatch; + for (auto* activeDOMObject : possibleActiveDOMObjects) { + // Check if this object was deleted already. If so, just skip it. + // Calling contains on a possibly-already-deleted object is OK because we guarantee + // no new object can be added, so even if a new object ends up allocated with the + // same address, that will be *after* this function exits. + if (!m_activeDOMObjects.contains(activeDOMObject)) + continue; + activeDOMObject->stop(); } - m_iteratingActiveDOMObjects = false; - // Also close MessagePorts. If they were ActiveDOMObjects (they could be) then they could be stopped instead. - closeMessagePorts(); + m_activeDOMObjectAdditionForbidden = false; + + // FIXME: Make message ports be active DOM objects and let them implement stop instead + // of having this separate mechanism just for them. + for (auto* messagePort : m_messagePorts) + messagePort->close(); } -void ScriptExecutionContext::suspendActiveDOMObjectIfNeeded(ActiveDOMObject* object) +void ScriptExecutionContext::suspendActiveDOMObjectIfNeeded(ActiveDOMObject& activeDOMObject) { - ASSERT(m_activeDOMObjects.contains(object)); - // Ensure all ActiveDOMObjects are suspended also newly created ones. + ASSERT(m_activeDOMObjects.contains(&activeDOMObject)); if (m_activeDOMObjectsAreSuspended) - object->suspend(m_reasonForSuspendingActiveDOMObjects); + activeDOMObject.suspend(m_reasonForSuspendingActiveDOMObjects); if (m_activeDOMObjectsAreStopped) - object->stop(); + activeDOMObject.stop(); } -void ScriptExecutionContext::didCreateActiveDOMObject(ActiveDOMObject* object) +void ScriptExecutionContext::didCreateActiveDOMObject(ActiveDOMObject& activeDOMObject) { - ASSERT(object); - ASSERT(!m_inDestructor); - if (m_iteratingActiveDOMObjects) - CRASH(); - m_activeDOMObjects.add(object); + // The m_activeDOMObjectAdditionForbidden check is a RELEASE_ASSERT because of the + // consequences of having an ActiveDOMObject that is not correctly reflected in the set. + // If we do have one of those, it can possibly be a security vulnerability. So we'd + // rather have a crash than continue running with the set possibly compromised. + ASSERT(!m_inScriptExecutionContextDestructor); + RELEASE_ASSERT(!m_activeDOMObjectAdditionForbidden); + m_activeDOMObjects.add(&activeDOMObject); } -void ScriptExecutionContext::willDestroyActiveDOMObject(ActiveDOMObject* object) +void ScriptExecutionContext::willDestroyActiveDOMObject(ActiveDOMObject& activeDOMObject) { - ASSERT(object); - if (m_iteratingActiveDOMObjects) - CRASH(); - m_activeDOMObjects.remove(object); + ASSERT_WITH_SECURITY_IMPLICATION(!m_activeDOMObjectRemovalForbidden); + m_activeDOMObjects.remove(&activeDOMObject); } -void ScriptExecutionContext::didCreateDestructionObserver(ContextDestructionObserver* observer) +void ScriptExecutionContext::didCreateDestructionObserver(ContextDestructionObserver& observer) { - ASSERT(observer); - ASSERT(!m_inDestructor); - m_destructionObservers.add(observer); + ASSERT(!m_inScriptExecutionContextDestructor); + m_destructionObservers.add(&observer); } -void ScriptExecutionContext::willDestroyDestructionObserver(ContextDestructionObserver* observer) +void ScriptExecutionContext::willDestroyDestructionObserver(ContextDestructionObserver& observer) { - ASSERT(observer); - m_destructionObservers.remove(observer); + m_destructionObservers.remove(&observer); } -void ScriptExecutionContext::closeMessagePorts() { - HashSet<MessagePort*>::iterator messagePortsEnd = m_messagePorts.end(); - for (HashSet<MessagePort*>::iterator iter = m_messagePorts.begin(); iter != messagePortsEnd; ++iter) { - ASSERT((*iter)->scriptExecutionContext() == this); - (*iter)->close(); - } -} - -bool ScriptExecutionContext::sanitizeScriptError(String& errorMessage, int& lineNumber, int& columnNumber, String& sourceURL, CachedScript* cachedScript) +bool ScriptExecutionContext::sanitizeScriptError(String& errorMessage, int& lineNumber, int& columnNumber, String& sourceURL, JSC::Strong<JSC::Unknown>& error, CachedScript* cachedScript) { - URL targetURL = completeURL(sourceURL); - if (securityOrigin()->canRequest(targetURL) || (cachedScript && cachedScript->passesAccessControlCheck(securityOrigin()))) + ASSERT(securityOrigin()); + if (cachedScript) { + ASSERT(cachedScript->origin()); + ASSERT(securityOrigin()->toString() == cachedScript->origin()->toString()); + if (cachedScript->isCORSSameOrigin()) + return false; + } else if (securityOrigin()->canRequest(completeURL(sourceURL))) return false; - errorMessage = "Script error."; - sourceURL = String(); + + errorMessage = ASCIILiteral { "Script error." }; + sourceURL = { }; lineNumber = 0; columnNumber = 0; + error = { }; return true; } -void ScriptExecutionContext::reportException(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, PassRefPtr<ScriptCallStack> callStack, CachedScript* cachedScript) +void ScriptExecutionContext::reportException(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, JSC::Exception* exception, RefPtr<ScriptCallStack>&& callStack, CachedScript* cachedScript) { if (m_inDispatchErrorEvent) { if (!m_pendingExceptions) - m_pendingExceptions = adoptPtr(new Vector<OwnPtr<PendingException>>()); - m_pendingExceptions->append(adoptPtr(new PendingException(errorMessage, lineNumber, columnNumber, sourceURL, callStack))); + m_pendingExceptions = std::make_unique<Vector<std::unique_ptr<PendingException>>>(); + m_pendingExceptions->append(std::make_unique<PendingException>(errorMessage, lineNumber, columnNumber, sourceURL, WTFMove(callStack))); return; } // First report the original exception and only then all the nested ones. - if (!dispatchErrorEvent(errorMessage, lineNumber, columnNumber, sourceURL, cachedScript)) - logExceptionToConsole(errorMessage, sourceURL, lineNumber, columnNumber, callStack); + if (!dispatchErrorEvent(errorMessage, lineNumber, columnNumber, sourceURL, exception, cachedScript)) + logExceptionToConsole(errorMessage, sourceURL, lineNumber, columnNumber, callStack.copyRef()); if (!m_pendingExceptions) return; - for (size_t i = 0; i < m_pendingExceptions->size(); i++) { - PendingException* e = m_pendingExceptions->at(i).get(); - logExceptionToConsole(e->m_errorMessage, e->m_sourceURL, e->m_lineNumber, e->m_columnNumber, e->m_callStack); - } - m_pendingExceptions.clear(); + auto pendingExceptions = WTFMove(m_pendingExceptions); + for (auto& exception : *pendingExceptions) + logExceptionToConsole(exception->m_errorMessage, exception->m_sourceURL, exception->m_lineNumber, exception->m_columnNumber, WTFMove(exception->m_callStack)); } void ScriptExecutionContext::addConsoleMessage(MessageSource source, MessageLevel level, const String& message, const String& sourceURL, unsigned lineNumber, unsigned columnNumber, JSC::ExecState* state, unsigned long requestIdentifier) @@ -325,16 +384,15 @@ void ScriptExecutionContext::addConsoleMessage(MessageSource source, MessageLeve addMessage(source, level, message, sourceURL, lineNumber, columnNumber, 0, state, requestIdentifier); } -bool ScriptExecutionContext::dispatchErrorEvent(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, CachedScript* cachedScript) +bool ScriptExecutionContext::dispatchErrorEvent(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, JSC::Exception* exception, CachedScript* cachedScript) { EventTarget* target = errorEventTarget(); if (!target) return false; #if PLATFORM(IOS) - if (target == target->toDOMWindow() && isDocument()) { - Settings* settings = static_cast<Document*>(this)->settings(); - if (settings && !settings->shouldDispatchJavaScriptWindowOnErrorEvents()) + if (target->toDOMWindow() && is<Document>(*this)) { + if (!downcast<Document>(*this).settings().shouldDispatchJavaScriptWindowOnErrorEvents()) return false; } #endif @@ -343,11 +401,12 @@ bool ScriptExecutionContext::dispatchErrorEvent(const String& errorMessage, int int line = lineNumber; int column = columnNumber; String sourceName = sourceURL; - sanitizeScriptError(message, line, column, sourceName, cachedScript); + JSC::Strong<JSC::Unknown> error = exception && exception->value() ? JSC::Strong<JSC::Unknown>(vm(), exception->value()) : JSC::Strong<JSC::Unknown>(); + sanitizeScriptError(message, line, column, sourceName, error, cachedScript); ASSERT(!m_inDispatchErrorEvent); m_inDispatchErrorEvent = true; - RefPtr<ErrorEvent> errorEvent = ErrorEvent::create(message, sourceName, line, column); + Ref<ErrorEvent> errorEvent = ErrorEvent::create(message, sourceName, line, column, error); target->dispatchEvent(errorEvent); m_inDispatchErrorEvent = false; return errorEvent->defaultPrevented(); @@ -361,70 +420,81 @@ int ScriptExecutionContext::circularSequentialID() return m_circularSequentialID; } -#if ENABLE(BLOB) PublicURLManager& ScriptExecutionContext::publicURLManager() { if (!m_publicURLManager) - m_publicURLManager = PublicURLManager::create(); + m_publicURLManager = PublicURLManager::create(this); return *m_publicURLManager; } -#endif -void ScriptExecutionContext::adjustMinimumTimerInterval(double oldMinimumTimerInterval) +void ScriptExecutionContext::adjustMinimumTimerInterval(std::chrono::milliseconds oldMinimumTimerInterval) { if (minimumTimerInterval() != oldMinimumTimerInterval) { - for (TimeoutMap::iterator iter = m_timeouts.begin(); iter != m_timeouts.end(); ++iter) { - DOMTimer* timer = iter->value; - timer->adjustMinimumTimerInterval(oldMinimumTimerInterval); - } + for (auto& timer : m_timeouts.values()) + timer->updateTimerIntervalIfNecessary(); } } -double ScriptExecutionContext::minimumTimerInterval() const +std::chrono::milliseconds ScriptExecutionContext::minimumTimerInterval() const { // The default implementation returns the DOMTimer's default // minimum timer interval. FIXME: to make it work with dedicated // workers, we will have to override it in the appropriate // subclass, and provide a way to enumerate a Document's dedicated // workers so we can update them all. - return Settings::defaultMinDOMTimerInterval(); + return DOMTimer::defaultMinimumInterval(); } void ScriptExecutionContext::didChangeTimerAlignmentInterval() { - for (TimeoutMap::iterator iter = m_timeouts.begin(); iter != m_timeouts.end(); ++iter) { - DOMTimer* timer = iter->value; + for (auto& timer : m_timeouts.values()) timer->didChangeAlignmentInterval(); - } } -double ScriptExecutionContext::timerAlignmentInterval() const +std::chrono::milliseconds ScriptExecutionContext::timerAlignmentInterval(bool) const { - return Settings::defaultDOMTimerAlignmentInterval(); + return DOMTimer::defaultAlignmentInterval(); } -ScriptExecutionContext::Task::~Task() +JSC::VM& ScriptExecutionContext::vm() { + if (is<Document>(*this)) + return commonVM(); + + return downcast<WorkerGlobalScope>(*this).script()->vm(); } -JSC::VM* ScriptExecutionContext::vm() +void ScriptExecutionContext::setDatabaseContext(DatabaseContext* databaseContext) { - if (isDocument()) - return JSDOMWindow::commonVM(); + m_databaseContext = databaseContext; +} + +bool ScriptExecutionContext::hasPendingActivity() const +{ + checkConsistency(); - if (isWorkerGlobalScope()) - return static_cast<WorkerGlobalScope*>(this)->script()->vm(); + for (auto* activeDOMObject : m_activeDOMObjects) { + if (activeDOMObject->hasPendingActivity()) + return true; + } + + for (auto* messagePort : m_messagePorts) { + if (messagePort->hasPendingActivity()) + return true; + } - ASSERT_NOT_REACHED(); - return 0; + return false; } -#if ENABLE(SQL_DATABASE) -void ScriptExecutionContext::setDatabaseContext(DatabaseContext* databaseContext) +JSC::ExecState* ScriptExecutionContext::execState() { - ASSERT(!m_databaseContext); - m_databaseContext = databaseContext; + if (is<Document>(*this)) { + Document& document = downcast<Document>(*this); + return execStateFromPage(mainThreadNormalWorld(), document.page()); + } + + WorkerGlobalScope* workerGlobalScope = static_cast<WorkerGlobalScope*>(this); + return execStateFromWorkerGlobalScope(workerGlobalScope); } -#endif } // namespace WebCore diff --git a/Source/WebCore/dom/ScriptExecutionContext.h b/Source/WebCore/dom/ScriptExecutionContext.h index 1c7f0dae1..ddf3471ef 100644 --- a/Source/WebCore/dom/ScriptExecutionContext.h +++ b/Source/WebCore/dom/ScriptExecutionContext.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2008, 2009, 2010, 2011, 2013, 2014, 2015, 2016 Apple Inc. All Rights Reserved. * Copyright (C) 2012 Google Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -25,37 +25,47 @@ * */ -#ifndef ScriptExecutionContext_h -#define ScriptExecutionContext_h +#pragma once #include "ActiveDOMObject.h" -#include "ConsoleTypes.h" -#include "URL.h" +#include "DOMTimer.h" #include "SecurityContext.h" #include "Supplementable.h" +#include <heap/HandleTypes.h> +#include <runtime/ConsoleTypes.h> +#include <wtf/CrossThreadTask.h> +#include <wtf/Function.h> #include <wtf/HashSet.h> namespace JSC { +class Exception; class ExecState; class VM; +template<typename> class Strong; +} + +namespace Inspector { +class ScriptCallStack; } namespace WebCore { class CachedScript; class DatabaseContext; -class DOMTimer; -class EventListener; class EventQueue; class EventTarget; class MessagePort; -class ScriptCallStack; - -#if ENABLE(BLOB) class PublicURLManager; -#endif +class ResourceRequest; +class SecurityOrigin; +class SocketProvider; +class URL; -class ScriptExecutionContext : public SecurityContext, public Supplementable<ScriptExecutionContext> { +namespace IDBClient { +class IDBConnectionProxy; +} + +class ScriptExecutionContext : public SecurityContext { public: ScriptExecutionContext(); virtual ~ScriptExecutionContext(); @@ -73,21 +83,31 @@ public: virtual void disableEval(const String& errorMessage) = 0; - bool sanitizeScriptError(String& errorMessage, int& lineNumber, int& columnNumber, String& sourceURL, CachedScript* = 0); - // FIXME: <http://webkit.org/b/114315> ScriptExecutionContext log exception should include a column number - void reportException(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, PassRefPtr<ScriptCallStack>, CachedScript* = 0); +#if ENABLE(INDEXED_DATABASE) + virtual IDBClient::IDBConnectionProxy* idbConnectionProxy() = 0; +#endif +#if ENABLE(WEB_SOCKETS) + virtual SocketProvider* socketProvider() = 0; +#endif + + virtual String resourceRequestIdentifier() const { return String(); }; - void addConsoleMessage(MessageSource, MessageLevel, const String& message, const String& sourceURL, unsigned lineNumber, unsigned columnNumber, JSC::ExecState* = 0, unsigned long requestIdentifier = 0); + bool sanitizeScriptError(String& errorMessage, int& lineNumber, int& columnNumber, String& sourceURL, JSC::Strong<JSC::Unknown>& error, CachedScript* = nullptr); + void reportException(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, JSC::Exception*, RefPtr<Inspector::ScriptCallStack>&&, CachedScript* = nullptr); + + void addConsoleMessage(MessageSource, MessageLevel, const String& message, const String& sourceURL, unsigned lineNumber, unsigned columnNumber, JSC::ExecState* = nullptr, unsigned long requestIdentifier = 0); virtual void addConsoleMessage(MessageSource, MessageLevel, const String& message, unsigned long requestIdentifier = 0) = 0; - virtual SecurityOrigin* topOrigin() const = 0; + virtual SecurityOrigin& topOrigin() const = 0; + + virtual bool shouldBypassMainWorldContentSecurityPolicy() const { return false; } -#if ENABLE(BLOB) PublicURLManager& publicURLManager(); -#endif + // Active objects are not garbage collected even if inaccessible, e.g. because their activity may result in callbacks being invoked. - bool canSuspendActiveDOMObjects(); - // Active objects can be asked to suspend even if canSuspendActiveDOMObjects() returns 'false' - + WEBCORE_EXPORT bool canSuspendActiveDOMObjectsForDocumentSuspension(Vector<ActiveDOMObject*>* unsuspendableObjects = nullptr); + + // Active objects can be asked to suspend even if canSuspendActiveDOMObjectsForDocumentSuspension() returns 'false' - // step-by-step JS debugging is one example. virtual void suspendActiveDOMObjects(ActiveDOMObject::ReasonForSuspension); virtual void resumeActiveDOMObjects(ActiveDOMObject::ReasonForSuspension); @@ -97,123 +117,155 @@ public: bool activeDOMObjectsAreStopped() const { return m_activeDOMObjectsAreStopped; } // Called from the constructor and destructors of ActiveDOMObject. - void didCreateActiveDOMObject(ActiveDOMObject*); - void willDestroyActiveDOMObject(ActiveDOMObject*); + void didCreateActiveDOMObject(ActiveDOMObject&); + void willDestroyActiveDOMObject(ActiveDOMObject&); // Called after the construction of an ActiveDOMObject to synchronize suspend state. - void suspendActiveDOMObjectIfNeeded(ActiveDOMObject*); - - typedef HashSet<ActiveDOMObject*> ActiveDOMObjectsSet; - const ActiveDOMObjectsSet& activeDOMObjects() const { return m_activeDOMObjects; } + void suspendActiveDOMObjectIfNeeded(ActiveDOMObject&); - void didCreateDestructionObserver(ContextDestructionObserver*); - void willDestroyDestructionObserver(ContextDestructionObserver*); + void didCreateDestructionObserver(ContextDestructionObserver&); + void willDestroyDestructionObserver(ContextDestructionObserver&); // MessagePort is conceptually a kind of ActiveDOMObject, but it needs to be tracked separately for message dispatch. void processMessagePortMessagesSoon(); void dispatchMessagePortEvents(); - void createdMessagePort(MessagePort*); - void destroyedMessagePort(MessagePort*); - const HashSet<MessagePort*>& messagePorts() const { return m_messagePorts; } + void createdMessagePort(MessagePort&); + void destroyedMessagePort(MessagePort&); + + virtual void didLoadResourceSynchronously(); void ref() { refScriptExecutionContext(); } void deref() { derefScriptExecutionContext(); } class Task { - WTF_MAKE_NONCOPYABLE(Task); WTF_MAKE_FAST_ALLOCATED; public: - Task() { } - virtual ~Task(); - virtual void performTask(ScriptExecutionContext*) = 0; - // Certain tasks get marked specially so that they aren't discarded, and are executed, when the context is shutting down its message queue. - virtual bool isCleanupTask() const { return false; } + enum CleanupTaskTag { CleanupTask }; + + template<typename T, typename = typename std::enable_if<!std::is_base_of<Task, T>::value && std::is_convertible<T, WTF::Function<void (ScriptExecutionContext&)>>::value>::type> + Task(T task) + : m_task(WTFMove(task)) + , m_isCleanupTask(false) + { + } + + Task(WTF::Function<void ()>&& task) + : m_task([task = WTFMove(task)](ScriptExecutionContext&) { task(); }) + , m_isCleanupTask(false) + { + } + + template<typename T, typename = typename std::enable_if<std::is_convertible<T, WTF::Function<void (ScriptExecutionContext&)>>::value>::type> + Task(CleanupTaskTag, T task) + : m_task(WTFMove(task)) + , m_isCleanupTask(true) + { + } + + void performTask(ScriptExecutionContext& context) { m_task(context); } + bool isCleanupTask() const { return m_isCleanupTask; } + + protected: + WTF::Function<void (ScriptExecutionContext&)> m_task; + bool m_isCleanupTask; }; - virtual void postTask(PassOwnPtr<Task>) = 0; // Executes the task on context's thread asynchronously. + virtual void postTask(Task&&) = 0; // Executes the task on context's thread asynchronously. + + template<typename... Arguments> + void postCrossThreadTask(Arguments&&... arguments) + { + postTask([crossThreadTask = createCrossThreadTask(arguments...)](ScriptExecutionContext&) mutable { + crossThreadTask.performTask(); + }); + } // Gets the next id in a circular sequence from 1 to 2^31-1. int circularSequentialID(); - bool addTimeout(int timeoutId, DOMTimer* timer) { return m_timeouts.add(timeoutId, timer).isNewEntry; } + bool addTimeout(int timeoutId, DOMTimer& timer) { return m_timeouts.add(timeoutId, &timer).isNewEntry; } void removeTimeout(int timeoutId) { m_timeouts.remove(timeoutId); } DOMTimer* findTimeout(int timeoutId) { return m_timeouts.get(timeoutId); } - JSC::VM* vm(); + WEBCORE_EXPORT JSC::VM& vm(); // Interval is in seconds. - void adjustMinimumTimerInterval(double oldMinimumTimerInterval); - virtual double minimumTimerInterval() const; + void adjustMinimumTimerInterval(std::chrono::milliseconds oldMinimumTimerInterval); + virtual std::chrono::milliseconds minimumTimerInterval() const; void didChangeTimerAlignmentInterval(); - virtual double timerAlignmentInterval() const; + virtual std::chrono::milliseconds timerAlignmentInterval(bool hasReachedMaxNestingLevel) const; virtual EventQueue& eventQueue() const = 0; -#if ENABLE(SQL_DATABASE) + DatabaseContext* databaseContext() { return m_databaseContext.get(); } void setDatabaseContext(DatabaseContext*); + +#if ENABLE(SUBTLE_CRYPTO) + virtual bool wrapCryptoKey(const Vector<uint8_t>& key, Vector<uint8_t>& wrappedKey) = 0; + virtual bool unwrapCryptoKey(const Vector<uint8_t>& wrappedKey, Vector<uint8_t>& key) = 0; #endif + int timerNestingLevel() const { return m_timerNestingLevel; } + void setTimerNestingLevel(int timerNestingLevel) { m_timerNestingLevel = timerNestingLevel; } + + JSC::ExecState* execState(); + protected: class AddConsoleMessageTask : public Task { public: - static PassOwnPtr<AddConsoleMessageTask> create(MessageSource source, MessageLevel level, const String& message) - { - return adoptPtr(new AddConsoleMessageTask(source, level, message)); - } - virtual void performTask(ScriptExecutionContext*) override; - private: AddConsoleMessageTask(MessageSource source, MessageLevel level, const String& message) - : m_source(source) - , m_level(level) - , m_message(message.isolatedCopy()) + : Task([source, level, message = message.isolatedCopy()](ScriptExecutionContext& context) { + context.addConsoleMessage(source, level, message); + }) { } - MessageSource m_source; - MessageLevel m_level; - String m_message; }; ActiveDOMObject::ReasonForSuspension reasonForSuspendingActiveDOMObjects() const { return m_reasonForSuspendingActiveDOMObjects; } + bool hasPendingActivity() const; + private: - virtual void addMessage(MessageSource, MessageLevel, const String& message, const String& sourceURL, unsigned lineNumber, unsigned columnNumber, PassRefPtr<ScriptCallStack>, JSC::ExecState* = 0, unsigned long requestIdentifier = 0) = 0; + virtual void addMessage(MessageSource, MessageLevel, const String& message, const String& sourceURL, unsigned lineNumber, unsigned columnNumber, RefPtr<Inspector::ScriptCallStack>&&, JSC::ExecState* = nullptr, unsigned long requestIdentifier = 0) = 0; virtual EventTarget* errorEventTarget() = 0; - virtual void logExceptionToConsole(const String& errorMessage, const String& sourceURL, int lineNumber, int columnNumber, PassRefPtr<ScriptCallStack>) = 0; - bool dispatchErrorEvent(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, CachedScript*); - - void closeMessagePorts(); + virtual void logExceptionToConsole(const String& errorMessage, const String& sourceURL, int lineNumber, int columnNumber, RefPtr<Inspector::ScriptCallStack>&&) = 0; + bool dispatchErrorEvent(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, JSC::Exception*, CachedScript*); virtual void refScriptExecutionContext() = 0; virtual void derefScriptExecutionContext() = 0; + void checkConsistency() const; + HashSet<MessagePort*> m_messagePorts; HashSet<ContextDestructionObserver*> m_destructionObservers; - ActiveDOMObjectsSet m_activeDOMObjects; - bool m_iteratingActiveDOMObjects; - bool m_inDestructor; + HashSet<ActiveDOMObject*> m_activeDOMObjects; - int m_circularSequentialID; - typedef HashMap<int, DOMTimer*> TimeoutMap; - TimeoutMap m_timeouts; + int m_circularSequentialID { 0 }; + HashMap<int, RefPtr<DOMTimer>> m_timeouts; - bool m_inDispatchErrorEvent; - class PendingException; - OwnPtr<Vector<OwnPtr<PendingException>>> m_pendingExceptions; + bool m_inDispatchErrorEvent { false }; + struct PendingException; + std::unique_ptr<Vector<std::unique_ptr<PendingException>>> m_pendingExceptions; - bool m_activeDOMObjectsAreSuspended; - ActiveDOMObject::ReasonForSuspension m_reasonForSuspendingActiveDOMObjects; - bool m_activeDOMObjectsAreStopped; + bool m_activeDOMObjectsAreSuspended { false }; + ActiveDOMObject::ReasonForSuspension m_reasonForSuspendingActiveDOMObjects { static_cast<ActiveDOMObject::ReasonForSuspension>(-1) }; + bool m_activeDOMObjectsAreStopped { false }; -#if ENABLE(BLOB) - OwnPtr<PublicURLManager> m_publicURLManager; -#endif + std::unique_ptr<PublicURLManager> m_publicURLManager; -#if ENABLE(SQL_DATABASE) RefPtr<DatabaseContext> m_databaseContext; + + bool m_activeDOMObjectAdditionForbidden { false }; + bool m_willProcessMessagePortMessagesSoon { false }; + int m_timerNestingLevel { 0 }; + +#if !ASSERT_DISABLED + bool m_inScriptExecutionContextDestructor { false }; +#endif +#if !ASSERT_DISABLED || ENABLE(SECURITY_ASSERTIONS) + bool m_activeDOMObjectRemovalForbidden { false }; #endif }; } // namespace WebCore - -#endif // ScriptExecutionContext_h diff --git a/Source/WebCore/dom/ScriptRunner.cpp b/Source/WebCore/dom/ScriptRunner.cpp index f48913d0b..0c666aa52 100644 --- a/Source/WebCore/dom/ScriptRunner.cpp +++ b/Source/WebCore/dom/ScriptRunner.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * Copyright (C) 2011-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,7 +27,6 @@ #include "config.h" #include "ScriptRunner.h" -#include "CachedScript.h" #include "Element.h" #include "PendingScript.h" #include "ScriptElement.h" @@ -35,40 +35,44 @@ namespace WebCore { ScriptRunner::ScriptRunner(Document& document) : m_document(document) - , m_timer(this, &ScriptRunner::timerFired) + , m_timer(*this, &ScriptRunner::timerFired) { } ScriptRunner::~ScriptRunner() { - for (size_t i = 0; i < m_scriptsToExecuteSoon.size(); ++i) + for (auto& pendingScript : m_scriptsToExecuteSoon) { + UNUSED_PARAM(pendingScript); m_document.decrementLoadEventDelayCount(); - for (size_t i = 0; i < m_scriptsToExecuteInOrder.size(); ++i) + } + for (auto& pendingScript : m_scriptsToExecuteInOrder) { + if (pendingScript->watchingForLoad()) + pendingScript->clearClient(); m_document.decrementLoadEventDelayCount(); - for (int i = 0; i < m_pendingAsyncScripts.size(); ++i) + } + for (auto& pendingScript : m_pendingAsyncScripts) { + if (pendingScript->watchingForLoad()) + pendingScript->clearClient(); m_document.decrementLoadEventDelayCount(); + } } -void ScriptRunner::queueScriptForExecution(ScriptElement* scriptElement, CachedResourceHandle<CachedScript> cachedScript, ExecutionType executionType) +void ScriptRunner::queueScriptForExecution(ScriptElement& scriptElement, LoadableScript& loadableScript, ExecutionType executionType) { - ASSERT(scriptElement); - ASSERT(cachedScript.get()); - - Element* element = scriptElement->element(); - ASSERT(element); - ASSERT(element->inDocument()); + ASSERT(scriptElement.element().isConnected()); m_document.incrementLoadEventDelayCount(); + auto pendingScript = PendingScript::create(scriptElement, loadableScript); switch (executionType) { case ASYNC_EXECUTION: - m_pendingAsyncScripts.add(scriptElement, PendingScript(element, cachedScript.get())); + m_pendingAsyncScripts.add(pendingScript.copyRef()); break; - case IN_ORDER_EXECUTION: - m_scriptsToExecuteInOrder.append(PendingScript(element, cachedScript.get())); + m_scriptsToExecuteInOrder.append(pendingScript.copyRef()); break; } + pendingScript->setClient(*this); } void ScriptRunner::suspend() @@ -82,41 +86,39 @@ void ScriptRunner::resume() m_timer.startOneShot(0); } -void ScriptRunner::notifyScriptReady(ScriptElement* scriptElement, ExecutionType executionType) +void ScriptRunner::notifyFinished(PendingScript& pendingScript) { - switch (executionType) { - case ASYNC_EXECUTION: - ASSERT(m_pendingAsyncScripts.contains(scriptElement)); - m_scriptsToExecuteSoon.append(m_pendingAsyncScripts.take(scriptElement)); - break; - - case IN_ORDER_EXECUTION: + if (pendingScript.element().willExecuteInOrder()) ASSERT(!m_scriptsToExecuteInOrder.isEmpty()); - break; + else { + ASSERT(m_pendingAsyncScripts.contains(pendingScript)); + m_scriptsToExecuteSoon.append(m_pendingAsyncScripts.take(pendingScript)->ptr()); } + pendingScript.clearClient(); m_timer.startOneShot(0); } -void ScriptRunner::timerFired(Timer<ScriptRunner>& timer) +void ScriptRunner::timerFired() { - ASSERT_UNUSED(timer, &timer == &m_timer); - Ref<Document> protect(m_document); - Vector<PendingScript> scripts; + Vector<RefPtr<PendingScript>> scripts; scripts.swap(m_scriptsToExecuteSoon); size_t numInOrderScriptsToExecute = 0; - for (; numInOrderScriptsToExecute < m_scriptsToExecuteInOrder.size() && m_scriptsToExecuteInOrder[numInOrderScriptsToExecute].cachedScript()->isLoaded(); ++numInOrderScriptsToExecute) - scripts.append(m_scriptsToExecuteInOrder[numInOrderScriptsToExecute]); + for (; numInOrderScriptsToExecute < m_scriptsToExecuteInOrder.size() && m_scriptsToExecuteInOrder[numInOrderScriptsToExecute]->isLoaded(); ++numInOrderScriptsToExecute) + scripts.append(m_scriptsToExecuteInOrder[numInOrderScriptsToExecute].ptr()); if (numInOrderScriptsToExecute) m_scriptsToExecuteInOrder.remove(0, numInOrderScriptsToExecute); - size_t size = scripts.size(); - for (size_t i = 0; i < size; ++i) { - CachedScript* cachedScript = scripts[i].cachedScript(); - RefPtr<Element> element = scripts[i].releaseElementAndClear(); - toScriptElementIfPossible(element.get())->execute(cachedScript); + for (auto& currentScript : scripts) { + auto script = WTFMove(currentScript); + ASSERT(script); + // Paper over https://bugs.webkit.org/show_bug.cgi?id=144050 + if (!script) + continue; + ASSERT(script->needsLoading()); + script->element().executePendingScript(*script); m_document.decrementLoadEventDelayCount(); } } diff --git a/Source/WebCore/dom/ScriptRunner.h b/Source/WebCore/dom/ScriptRunner.h index f23e6da0f..8e8f486ad 100644 --- a/Source/WebCore/dom/ScriptRunner.h +++ b/Source/WebCore/dom/ScriptRunner.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * Copyright (C) 2011-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,47 +24,42 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ScriptRunner_h -#define ScriptRunner_h +#pragma once -#include "CachedResourceHandle.h" +#include "PendingScriptClient.h" #include "Timer.h" -#include <wtf/HashMap.h> -#include <wtf/Noncopyable.h> -#include <wtf/PassOwnPtr.h> -#include <wtf/PassRefPtr.h> +#include <wtf/HashSet.h> #include <wtf/Vector.h> namespace WebCore { -class CachedScript; class Document; -class PendingScript; class ScriptElement; +class LoadableScript; -class ScriptRunner { +class ScriptRunner : private PendingScriptClient { WTF_MAKE_NONCOPYABLE(ScriptRunner); WTF_MAKE_FAST_ALLOCATED; public: explicit ScriptRunner(Document&); ~ScriptRunner(); enum ExecutionType { ASYNC_EXECUTION, IN_ORDER_EXECUTION }; - void queueScriptForExecution(ScriptElement*, CachedResourceHandle<CachedScript>, ExecutionType); + void queueScriptForExecution(ScriptElement&, LoadableScript&, ExecutionType); bool hasPendingScripts() const { return !m_scriptsToExecuteSoon.isEmpty() || !m_scriptsToExecuteInOrder.isEmpty() || !m_pendingAsyncScripts.isEmpty(); } void suspend(); void resume(); void notifyScriptReady(ScriptElement*, ExecutionType); private: - void timerFired(Timer<ScriptRunner>&); + void timerFired(); + + void notifyFinished(PendingScript&) override; Document& m_document; - Vector<PendingScript> m_scriptsToExecuteInOrder; - Vector<PendingScript> m_scriptsToExecuteSoon; // http://www.whatwg.org/specs/web-apps/current-work/#set-of-scripts-that-will-execute-as-soon-as-possible - HashMap<ScriptElement*, PendingScript> m_pendingAsyncScripts; - Timer<ScriptRunner> m_timer; + Vector<Ref<PendingScript>> m_scriptsToExecuteInOrder; + Vector<RefPtr<PendingScript>> m_scriptsToExecuteSoon; // http://www.whatwg.org/specs/web-apps/current-work/#set-of-scripts-that-will-execute-as-soon-as-possible + HashSet<Ref<PendingScript>> m_pendingAsyncScripts; + Timer m_timer; }; } - -#endif diff --git a/Source/WebCore/dom/ScriptableDocumentParser.cpp b/Source/WebCore/dom/ScriptableDocumentParser.cpp index a6fa08124..166b29fc7 100644 --- a/Source/WebCore/dom/ScriptableDocumentParser.cpp +++ b/Source/WebCore/dom/ScriptableDocumentParser.cpp @@ -27,9 +27,8 @@ #include "ScriptableDocumentParser.h" #include "Document.h" -#include "Frame.h" -#include "ScriptController.h" #include "Settings.h" +#include "StyleScope.h" namespace WebCore { @@ -37,12 +36,45 @@ ScriptableDocumentParser::ScriptableDocumentParser(Document& document, ParserCon : DecodedDataDocumentParser(document) , m_wasCreatedByScript(false) , m_parserContentPolicy(parserContentPolicy) + , m_scriptsWaitingForStylesheetsExecutionTimer(*this, &ScriptableDocumentParser::scriptsWaitingForStylesheetsExecutionTimerFired) { - if (!pluginContentIsAllowed(m_parserContentPolicy) && (!document.settings() || document.settings()->unsafePluginPastingEnabled())) + if (!pluginContentIsAllowed(m_parserContentPolicy)) m_parserContentPolicy = allowPluginContent(m_parserContentPolicy); - if (scriptingContentIsAllowed(m_parserContentPolicy) && (document.settings() && !document.settings()->scriptMarkupEnabled())) + if (scriptingContentIsAllowed(m_parserContentPolicy) && !document.settings().scriptMarkupEnabled()) m_parserContentPolicy = disallowScriptingContent(m_parserContentPolicy); } +void ScriptableDocumentParser::executeScriptsWaitingForStylesheetsSoon() +{ + ASSERT(!document()->styleScope().hasPendingSheets()); + + if (m_scriptsWaitingForStylesheetsExecutionTimer.isActive()) + return; + if (!hasScriptsWaitingForStylesheets()) + return; + + m_scriptsWaitingForStylesheetsExecutionTimer.startOneShot(0); +} + +void ScriptableDocumentParser::scriptsWaitingForStylesheetsExecutionTimerFired() +{ + ASSERT(!isDetached()); + + Ref<ScriptableDocumentParser> protectedThis(*this); + + if (!document()->styleScope().hasPendingSheets()) + executeScriptsWaitingForStylesheets(); + + if (!isDetached()) + document()->checkCompleted(); +} + +void ScriptableDocumentParser::detach() +{ + m_scriptsWaitingForStylesheetsExecutionTimer.stop(); + + DecodedDataDocumentParser::detach(); +} + }; diff --git a/Source/WebCore/dom/ScriptableDocumentParser.h b/Source/WebCore/dom/ScriptableDocumentParser.h index 8f8dc98c8..8fc1c9ca0 100644 --- a/Source/WebCore/dom/ScriptableDocumentParser.h +++ b/Source/WebCore/dom/ScriptableDocumentParser.h @@ -23,11 +23,11 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ScriptableDocumentParser_h -#define ScriptableDocumentParser_h +#pragma once #include "DecodedDataDocumentParser.h" #include "FragmentScriptingPermission.h" +#include "Timer.h" #include <wtf/text/TextPosition.h> namespace WebCore { @@ -38,14 +38,18 @@ public: // JavaScript document.open() call right now, or it should be ignored. virtual bool isExecutingScript() const { return false; } - // FIXME: Only the HTMLDocumentParser ever blocks script execution on - // stylesheet load, which is likely a bug in the XMLDocumentParser. - virtual void executeScriptsWaitingForStylesheets() { } - virtual bool isWaitingForScripts() const = 0; virtual TextPosition textPosition() const = 0; + virtual bool hasScriptsWaitingForStylesheets() const { return false; } + + void executeScriptsWaitingForStylesheetsSoon(); + + // Returns true if the parser didn't yield or pause or synchronously execute a script, + // so calls to PageConsoleClient should be associated with the parser's text position. + virtual bool shouldAssociateConsoleMessagesWithTextPosition() const = 0; + void setWasCreatedByScript(bool wasCreatedByScript) { m_wasCreatedByScript = wasCreatedByScript; } bool wasCreatedByScript() const { return m_wasCreatedByScript; } @@ -54,14 +58,19 @@ public: protected: explicit ScriptableDocumentParser(Document&, ParserContentPolicy = AllowScriptingContent); + virtual void executeScriptsWaitingForStylesheets() { } + + void detach() override; + private: - virtual ScriptableDocumentParser* asScriptableDocumentParser() override { return this; } + ScriptableDocumentParser* asScriptableDocumentParser() final { return this; } + + void scriptsWaitingForStylesheetsExecutionTimerFired(); // http://www.whatwg.org/specs/web-apps/current-work/#script-created-parser bool m_wasCreatedByScript; ParserContentPolicy m_parserContentPolicy; + Timer m_scriptsWaitingForStylesheetsExecutionTimer; }; -} - -#endif // ScriptableDocumentParser_h +} // namespace WebCore diff --git a/Source/WebCore/dom/ScriptedAnimationController.cpp b/Source/WebCore/dom/ScriptedAnimationController.cpp index ca582addd..b94ebdf36 100644 --- a/Source/WebCore/dom/ScriptedAnimationController.cpp +++ b/Source/WebCore/dom/ScriptedAnimationController.cpp @@ -26,15 +26,20 @@ #include "config.h" #include "ScriptedAnimationController.h" -#if ENABLE(REQUEST_ANIMATION_FRAME) - +#include "DOMWindow.h" +#include "DisplayRefreshMonitor.h" +#include "DisplayRefreshMonitorManager.h" #include "Document.h" #include "DocumentLoader.h" #include "FrameView.h" #include "InspectorInstrumentation.h" +#include "Logging.h" +#include "MainFrame.h" +#include "Page.h" #include "RequestAnimationFrameCallback.h" #include "Settings.h" #include <wtf/Ref.h> +#include <wtf/SystemTracing.h> #if USE(REQUEST_ANIMATION_FRAME_TIMER) #include <algorithm> @@ -49,15 +54,8 @@ namespace WebCore { ScriptedAnimationController::ScriptedAnimationController(Document* document, PlatformDisplayID displayID) : m_document(document) - , m_nextCallbackId(0) - , m_suspendCount(0) #if USE(REQUEST_ANIMATION_FRAME_TIMER) - , m_animationTimer(this, &ScriptedAnimationController::animationTimerFired) - , m_lastAnimationFrameTimeMonotonic(0) -#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) - , m_isUsingTimer(false) - , m_isThrottled(false) -#endif + , m_animationTimer(*this, &ScriptedAnimationController::animationTimerFired) #endif { windowScreenDidChange(displayID); @@ -67,6 +65,11 @@ ScriptedAnimationController::~ScriptedAnimationController() { } +bool ScriptedAnimationController::requestAnimationFrameEnabled() const +{ + return m_document && m_document->settings().requestAnimationFrameEnabled(); +} + void ScriptedAnimationController::suspend() { ++m_suspendCount; @@ -89,6 +92,8 @@ void ScriptedAnimationController::setThrottled(bool isThrottled) if (m_isThrottled == isThrottled) return; + LOG(Animations, "%p - Setting RequestAnimationFrame throttling state to %d in frame %p (isMainFrame: %d)", this, isThrottled, m_document->frame(), m_document->frame() ? m_document->frame()->isMainFrame() : 0); + m_isThrottled = isThrottled; if (m_animationTimer.isActive()) { m_animationTimer.stop(); @@ -99,12 +104,21 @@ void ScriptedAnimationController::setThrottled(bool isThrottled) #endif } -ScriptedAnimationController::CallbackId ScriptedAnimationController::registerCallback(PassRefPtr<RequestAnimationFrameCallback> callback) +bool ScriptedAnimationController::isThrottled() const +{ +#if USE(REQUEST_ANIMATION_FRAME_TIMER) && USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) + return m_isThrottled; +#else + return false; +#endif +} + +ScriptedAnimationController::CallbackId ScriptedAnimationController::registerCallback(Ref<RequestAnimationFrameCallback>&& callback) { ScriptedAnimationController::CallbackId id = ++m_nextCallbackId; callback->m_firedOrCancelled = false; callback->m_id = id; - m_callbacks.append(callback); + m_callbacks.append(WTFMove(callback)); InspectorInstrumentation::didRequestAnimationFrame(m_document, id); @@ -125,13 +139,15 @@ void ScriptedAnimationController::cancelCallback(CallbackId id) } } -void ScriptedAnimationController::serviceScriptedAnimations(double monotonicTimeNow) +void ScriptedAnimationController::serviceScriptedAnimations(double timestamp) { - if (!m_callbacks.size() || m_suspendCount || (m_document->settings() && !m_document->settings()->requestAnimationFrameEnabled())) + if (!m_callbacks.size() || m_suspendCount || !requestAnimationFrameEnabled()) return; - double highResNowMs = 1000.0 * m_document->loader()->timing()->monotonicTimeToZeroBasedDocumentTime(monotonicTimeNow); - double legacyHighResNowMs = 1000.0 * m_document->loader()->timing()->monotonicTimeToPseudoWallTime(monotonicTimeNow); + TraceScope tracingScope(RAFCallbackStart, RAFCallbackEnd); + + double highResNowMs = 1000 * timestamp; + double legacyHighResNowMs = 1000 * (timestamp + m_document->loader()->timing().referenceWallTime().secondsSinceEpoch().seconds()); // First, generate a list of callbacks to consider. Callbacks registered from this point // on are considered only for the "next" frame, not this one. @@ -139,10 +155,9 @@ void ScriptedAnimationController::serviceScriptedAnimations(double monotonicTime // Invoking callbacks may detach elements from our document, which clears the document's // reference to us, so take a defensive reference. - Ref<ScriptedAnimationController> protect(*this); + Ref<ScriptedAnimationController> protectedThis(*this); - for (size_t i = 0; i < callbacks.size(); ++i) { - RequestAnimationFrameCallback* callback = callbacks[i].get(); + for (auto& callback : callbacks) { if (!callback->m_firedOrCancelled) { callback->m_firedOrCancelled = true; InspectorInstrumentationCookie cookie = InspectorInstrumentation::willFireAnimationFrame(m_document, callback->m_id); @@ -168,10 +183,10 @@ void ScriptedAnimationController::serviceScriptedAnimations(double monotonicTime void ScriptedAnimationController::windowScreenDidChange(PlatformDisplayID displayID) { - if (m_document->settings() && !m_document->settings()->requestAnimationFrameEnabled()) + if (!requestAnimationFrameEnabled()) return; #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) - DisplayRefreshMonitorManager::sharedManager()->windowScreenDidChange(displayID, this); + DisplayRefreshMonitorManager::sharedManager().windowScreenDidChange(displayID, *this); #else UNUSED_PARAM(displayID); #endif @@ -179,13 +194,13 @@ void ScriptedAnimationController::windowScreenDidChange(PlatformDisplayID displa void ScriptedAnimationController::scheduleAnimation() { - if (!m_document || (m_document->settings() && !m_document->settings()->requestAnimationFrameEnabled())) + if (!requestAnimationFrameEnabled()) return; #if USE(REQUEST_ANIMATION_FRAME_TIMER) #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) if (!m_isUsingTimer && !m_isThrottled) { - if (DisplayRefreshMonitorManager::sharedManager()->scheduleAnimation(this)) + if (DisplayRefreshMonitorManager::sharedManager().scheduleAnimation(*this)) return; m_isUsingTimer = true; @@ -200,7 +215,7 @@ void ScriptedAnimationController::scheduleAnimation() animationInterval = MinimumThrottledAnimationInterval; #endif - double scheduleDelay = std::max<double>(animationInterval - (monotonicallyIncreasingTime() - m_lastAnimationFrameTimeMonotonic), 0); + double scheduleDelay = std::max<double>(animationInterval - (m_document->domWindow()->nowTimestamp() - m_lastAnimationFrameTimestamp), 0); m_animationTimer.startOneShot(scheduleDelay); #else if (FrameView* frameView = m_document->view()) @@ -209,21 +224,31 @@ void ScriptedAnimationController::scheduleAnimation() } #if USE(REQUEST_ANIMATION_FRAME_TIMER) -void ScriptedAnimationController::animationTimerFired(Timer<ScriptedAnimationController>&) +void ScriptedAnimationController::animationTimerFired() { - m_lastAnimationFrameTimeMonotonic = monotonicallyIncreasingTime(); - serviceScriptedAnimations(m_lastAnimationFrameTimeMonotonic); + m_lastAnimationFrameTimestamp = m_document->domWindow()->nowTimestamp(); + serviceScriptedAnimations(m_lastAnimationFrameTimestamp); } #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) -void ScriptedAnimationController::displayRefreshFired(double monotonicTimeNow) +void ScriptedAnimationController::displayRefreshFired() { - serviceScriptedAnimations(monotonicTimeNow); + serviceScriptedAnimations(m_document->domWindow()->nowTimestamp()); } #endif #endif +#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) +RefPtr<DisplayRefreshMonitor> ScriptedAnimationController::createDisplayRefreshMonitor(PlatformDisplayID displayID) const +{ + if (!m_document->page()) + return nullptr; + + if (auto monitor = m_document->page()->chrome().client().createDisplayRefreshMonitor(displayID)) + return monitor; + return DisplayRefreshMonitor::createDefaultDisplayRefreshMonitor(displayID); } - #endif + +} diff --git a/Source/WebCore/dom/ScriptedAnimationController.h b/Source/WebCore/dom/ScriptedAnimationController.h index f2808e78d..012c25862 100644 --- a/Source/WebCore/dom/ScriptedAnimationController.h +++ b/Source/WebCore/dom/ScriptedAnimationController.h @@ -23,22 +23,24 @@ * */ -#ifndef ScriptedAnimationController_h -#define ScriptedAnimationController_h +#pragma once -#if ENABLE(REQUEST_ANIMATION_FRAME) #include "DOMTimeStamp.h" -#if USE(REQUEST_ANIMATION_FRAME_TIMER) -#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) -#include "DisplayRefreshMonitor.h" -#endif -#include "Timer.h" -#endif #include "PlatformScreen.h" #include <wtf/RefCounted.h> #include <wtf/RefPtr.h> #include <wtf/Vector.h> +#if USE(REQUEST_ANIMATION_FRAME_TIMER) +#include "Timer.h" +#endif + +#if USE(REQUEST_ANIMATION_FRAME_TIMER) && USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) +#include "Chrome.h" +#include "ChromeClient.h" +#include "DisplayRefreshMonitorClient.h" +#endif + namespace WebCore { class Document; @@ -50,22 +52,24 @@ class ScriptedAnimationController : public RefCounted<ScriptedAnimationControlle #endif { public: - static PassRefPtr<ScriptedAnimationController> create(Document* document, PlatformDisplayID displayID) + static Ref<ScriptedAnimationController> create(Document* document, PlatformDisplayID displayID) { - return adoptRef(new ScriptedAnimationController(document, displayID)); + return adoptRef(*new ScriptedAnimationController(document, displayID)); } ~ScriptedAnimationController(); - void clearDocumentPointer() { m_document = 0; } + void clearDocumentPointer() { m_document = nullptr; } + bool requestAnimationFrameEnabled() const; typedef int CallbackId; - CallbackId registerCallback(PassRefPtr<RequestAnimationFrameCallback>); + CallbackId registerCallback(Ref<RequestAnimationFrameCallback>&&); void cancelCallback(CallbackId); - void serviceScriptedAnimations(double monotonicTimeNow); + void serviceScriptedAnimations(double timestamp); void suspend(); void resume(); void setThrottled(bool); + WEBCORE_EXPORT bool isThrottled() const; void windowScreenDidChange(PlatformDisplayID); @@ -76,28 +80,25 @@ private: CallbackList m_callbacks; Document* m_document; - CallbackId m_nextCallbackId; - int m_suspendCount; + CallbackId m_nextCallbackId { 0 }; + int m_suspendCount { 0 }; void scheduleAnimation(); #if USE(REQUEST_ANIMATION_FRAME_TIMER) - void animationTimerFired(Timer<ScriptedAnimationController>&); - Timer<ScriptedAnimationController> m_animationTimer; - double m_lastAnimationFrameTimeMonotonic; + void animationTimerFired(); + Timer m_animationTimer; + double m_lastAnimationFrameTimestamp { 0 }; +#endif -#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) +#if USE(REQUEST_ANIMATION_FRAME_TIMER) && USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) // Override for DisplayRefreshMonitorClient - virtual void displayRefreshFired(double timestamp) override; + void displayRefreshFired() override; + RefPtr<DisplayRefreshMonitor> createDisplayRefreshMonitor(PlatformDisplayID) const override; - bool m_isUsingTimer; - bool m_isThrottled; -#endif + bool m_isUsingTimer { false }; + bool m_isThrottled { false }; #endif }; -} - -#endif // ENABLE(REQUEST_ANIMATION_FRAME) - -#endif // ScriptedAnimationController_h +} // namespace WebCore diff --git a/Source/WebCore/dom/SecurityContext.cpp b/Source/WebCore/dom/SecurityContext.cpp index d83fdf22c..af29a164b 100644 --- a/Source/WebCore/dom/SecurityContext.cpp +++ b/Source/WebCore/dom/SecurityContext.cpp @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY GOOGLE, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -30,13 +30,14 @@ #include "ContentSecurityPolicy.h" #include "HTMLParserIdioms.h" #include "SecurityOrigin.h" +#include "SecurityOriginPolicy.h" +#include <wtf/NeverDestroyed.h> #include <wtf/text/StringBuilder.h> namespace WebCore { SecurityContext::SecurityContext() - : m_mayDisplaySeamlesslyWithParent(false) - , m_haveInitializedSecurityOrigin(false) + : m_haveInitializedSecurityOrigin(false) , m_sandboxFlags(SandboxNone) { } @@ -45,15 +46,23 @@ SecurityContext::~SecurityContext() { } -void SecurityContext::setSecurityOrigin(PassRefPtr<SecurityOrigin> securityOrigin) +void SecurityContext::setSecurityOriginPolicy(RefPtr<SecurityOriginPolicy>&& securityOriginPolicy) { - m_securityOrigin = securityOrigin; + m_securityOriginPolicy = WTFMove(securityOriginPolicy); m_haveInitializedSecurityOrigin = true; } -void SecurityContext::setContentSecurityPolicy(PassOwnPtr<ContentSecurityPolicy> contentSecurityPolicy) +SecurityOrigin* SecurityContext::securityOrigin() const { - m_contentSecurityPolicy = contentSecurityPolicy; + if (!m_securityOriginPolicy) + return nullptr; + + return &m_securityOriginPolicy->origin(); +} + +void SecurityContext::setContentSecurityPolicy(std::unique_ptr<ContentSecurityPolicy> contentSecurityPolicy) +{ + m_contentSecurityPolicy = WTFMove(contentSecurityPolicy); } bool SecurityContext::isSecureTransitionTo(const URL& url) const @@ -64,8 +73,7 @@ bool SecurityContext::isSecureTransitionTo(const URL& url) const if (!haveInitializedSecurityOrigin()) return true; - RefPtr<SecurityOrigin> other = SecurityOrigin::create(url); - return securityOrigin()->canAccess(other.get()); + return securityOriginPolicy()->origin().canAccess(SecurityOrigin::create(url).get()); } void SecurityContext::enforceSandboxFlags(SandboxFlags mask) @@ -73,10 +81,24 @@ void SecurityContext::enforceSandboxFlags(SandboxFlags mask) m_sandboxFlags |= mask; // The SandboxOrigin is stored redundantly in the security origin. - if (isSandboxed(SandboxOrigin) && securityOrigin() && !securityOrigin()->isUnique()) - setSecurityOrigin(SecurityOrigin::createUnique()); + if (isSandboxed(SandboxOrigin) && securityOriginPolicy() && !securityOriginPolicy()->origin().isUnique()) + setSecurityOriginPolicy(SecurityOriginPolicy::create(SecurityOrigin::createUnique())); +} + +bool SecurityContext::isSupportedSandboxPolicy(StringView policy) +{ + static const char* const supportedPolicies[] = { + "allow-forms", "allow-same-origin", "allow-scripts", "allow-top-navigation", "allow-pointer-lock", "allow-popups" + }; + + for (auto* supportedPolicy : supportedPolicies) { + if (equalIgnoringASCIICase(policy, supportedPolicy)) + return true; + } + return false; } +// Keep SecurityContext::isSupportedSandboxPolicy() in sync when updating this function. SandboxFlags SecurityContext::parseSandboxPolicy(const String& policy, String& invalidTokensErrorMessage) { // http://www.w3.org/TR/html5/the-iframe-element.html#attr-iframe-sandbox @@ -97,18 +119,18 @@ SandboxFlags SecurityContext::parseSandboxPolicy(const String& policy, String& i // Turn off the corresponding sandbox flag if it's set as "allowed". String sandboxToken = policy.substring(start, end - start); - if (equalIgnoringCase(sandboxToken, "allow-same-origin")) + if (equalLettersIgnoringASCIICase(sandboxToken, "allow-same-origin")) flags &= ~SandboxOrigin; - else if (equalIgnoringCase(sandboxToken, "allow-forms")) + else if (equalLettersIgnoringASCIICase(sandboxToken, "allow-forms")) flags &= ~SandboxForms; - else if (equalIgnoringCase(sandboxToken, "allow-scripts")) { + else if (equalLettersIgnoringASCIICase(sandboxToken, "allow-scripts")) { flags &= ~SandboxScripts; flags &= ~SandboxAutomaticFeatures; - } else if (equalIgnoringCase(sandboxToken, "allow-top-navigation")) + } else if (equalLettersIgnoringASCIICase(sandboxToken, "allow-top-navigation")) flags &= ~SandboxTopNavigation; - else if (equalIgnoringCase(sandboxToken, "allow-popups")) + else if (equalLettersIgnoringASCIICase(sandboxToken, "allow-popups")) flags &= ~SandboxPopups; - else if (equalIgnoringCase(sandboxToken, "allow-pointer-lock")) + else if (equalLettersIgnoringASCIICase(sandboxToken, "allow-pointer-lock")) flags &= ~SandboxPointerLock; else { if (numberOfTokenErrors) diff --git a/Source/WebCore/dom/SecurityContext.h b/Source/WebCore/dom/SecurityContext.h index 2db5d0d29..23976f66d 100644 --- a/Source/WebCore/dom/SecurityContext.h +++ b/Source/WebCore/dom/SecurityContext.h @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY GOOGLE, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,16 +24,16 @@ * */ -#ifndef SecurityContext_h -#define SecurityContext_h +#pragma once -#include <wtf/PassRefPtr.h> +#include <memory> +#include <wtf/Forward.h> #include <wtf/RefPtr.h> -#include <wtf/text/WTFString.h> namespace WebCore { class SecurityOrigin; +class SecurityOriginPolicy; class ContentSecurityPolicy; class URL; @@ -48,8 +48,7 @@ enum SandboxFlag { SandboxTopNavigation = 1 << 5, SandboxPopups = 1 << 6, // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=12393 SandboxAutomaticFeatures = 1 << 7, - SandboxSeamlessIframes = 1 << 8, - SandboxPointerLock = 1 << 9, + SandboxPointerLock = 1 << 8, SandboxAll = -1 // Mask with all bits set to 1. }; @@ -57,7 +56,6 @@ typedef int SandboxFlags; class SecurityContext { public: - SecurityOrigin* securityOrigin() const { return m_securityOrigin.get(); } SandboxFlags sandboxFlags() const { return m_sandboxFlags; } ContentSecurityPolicy* contentSecurityPolicy() { return m_contentSecurityPolicy.get(); } @@ -66,33 +64,43 @@ public: void enforceSandboxFlags(SandboxFlags mask); bool isSandboxed(SandboxFlags mask) const { return m_sandboxFlags & mask; } + SecurityOriginPolicy* securityOriginPolicy() const { return m_securityOriginPolicy.get(); } + // Explicitly override the security origin for this security context. // Note: It is dangerous to change the security origin of a script context // that already contains content. - void setSecurityOrigin(PassRefPtr<SecurityOrigin>); + void setSecurityOriginPolicy(RefPtr<SecurityOriginPolicy>&&); + + WEBCORE_EXPORT SecurityOrigin* securityOrigin() const; static SandboxFlags parseSandboxPolicy(const String& policy, String& invalidTokensErrorMessage); + static bool isSupportedSandboxPolicy(StringView); + + bool foundMixedContent() const { return m_foundMixedContent; } + void setFoundMixedContent() { m_foundMixedContent = true; } + bool geolocationAccessed() const { return m_geolocationAccessed; } + void setGeolocationAccessed() { m_geolocationAccessed = true; } + + bool isStrictMixedContentMode() const { return m_isStrictMixedContentMode; } + void setStrictMixedContentMode(bool strictMixedContentMode) { m_isStrictMixedContentMode = strictMixedContentMode; } protected: SecurityContext(); virtual ~SecurityContext(); - void setContentSecurityPolicy(PassOwnPtr<ContentSecurityPolicy>); + void setContentSecurityPolicy(std::unique_ptr<ContentSecurityPolicy>); void didFailToInitializeSecurityOrigin() { m_haveInitializedSecurityOrigin = false; } bool haveInitializedSecurityOrigin() const { return m_haveInitializedSecurityOrigin; } - // Set in Document::initSecurityContext() at Document creation, per: - // http://www.whatwg.org/specs/web-apps/current-work/#attr-iframe-seamless - bool m_mayDisplaySeamlesslyWithParent; - private: bool m_haveInitializedSecurityOrigin; SandboxFlags m_sandboxFlags; - RefPtr<SecurityOrigin> m_securityOrigin; - OwnPtr<ContentSecurityPolicy> m_contentSecurityPolicy; + RefPtr<SecurityOriginPolicy> m_securityOriginPolicy; + std::unique_ptr<ContentSecurityPolicy> m_contentSecurityPolicy; + bool m_foundMixedContent { false }; + bool m_geolocationAccessed { false }; + bool m_isStrictMixedContentMode { false }; }; } // namespace WebCore - -#endif // SecurityContext_h diff --git a/Source/WebCore/dom/SecurityOriginPolicy.cpp b/Source/WebCore/dom/SecurityOriginPolicy.cpp new file mode 100644 index 000000000..f490445f6 --- /dev/null +++ b/Source/WebCore/dom/SecurityOriginPolicy.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "SecurityOriginPolicy.h" + +#include "SecurityOrigin.h" + +namespace WebCore { + +Ref<SecurityOriginPolicy> SecurityOriginPolicy::create(Ref<SecurityOrigin>&& securityOrigin) +{ + return adoptRef(*new SecurityOriginPolicy(WTFMove(securityOrigin))); +} + +SecurityOriginPolicy::SecurityOriginPolicy(Ref<SecurityOrigin>&& securityOrigin) + : m_securityOrigin(WTFMove(securityOrigin)) +{ +} + +SecurityOriginPolicy::~SecurityOriginPolicy() +{ +} + +} diff --git a/Source/WebCore/dom/SecurityOriginPolicy.h b/Source/WebCore/dom/SecurityOriginPolicy.h new file mode 100644 index 000000000..a303d9b9c --- /dev/null +++ b/Source/WebCore/dom/SecurityOriginPolicy.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <wtf/Ref.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class SecurityOrigin; + +class SecurityOriginPolicy : public RefCounted<SecurityOriginPolicy> { +public: + static Ref<SecurityOriginPolicy> create(Ref<SecurityOrigin>&&); + ~SecurityOriginPolicy(); + + // FIXME: This should return a const reference. + SecurityOrigin& origin() { return m_securityOrigin; } + +private: + explicit SecurityOriginPolicy(Ref<SecurityOrigin>&&); + + Ref<SecurityOrigin> m_securityOrigin; +}; + +} // namespace WebCore diff --git a/Source/WebCore/dom/SecurityPolicyViolationEvent.h b/Source/WebCore/dom/SecurityPolicyViolationEvent.h index dca814cce..a6032f2e0 100644 --- a/Source/WebCore/dom/SecurityPolicyViolationEvent.h +++ b/Source/WebCore/dom/SecurityPolicyViolationEvent.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2013 Google Inc. All rights reserved. + * Copyright (C) 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -22,41 +23,40 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SecurityPolicyViolationEvent_h -#define SecurityPolicyViolationEvent_h - -#if ENABLE(CSP_NEXT) +#pragma once #include "Event.h" -#include "EventNames.h" namespace WebCore { -struct SecurityPolicyViolationEventInit : public EventInit { - SecurityPolicyViolationEventInit() +class SecurityPolicyViolationEvent final : public Event { +public: + static Ref<SecurityPolicyViolationEvent> create(const AtomicString& type, bool canBubble, bool cancelable, const String& documentURI, const String& referrer, const String& blockedURI, const String& violatedDirective, const String& effectiveDirective, const String& originalPolicy, const String& sourceFile, unsigned short statusCode, int lineNumber, int columnNumber) { + return adoptRef(*new SecurityPolicyViolationEvent(type, canBubble, cancelable, documentURI, referrer, blockedURI, violatedDirective, effectiveDirective, originalPolicy, sourceFile, statusCode, lineNumber, columnNumber)); } - String documentURI; - String referrer; - String blockedURI; - String violatedDirective; - String effectiveDirective; - String originalPolicy; - String sourceFile; - int lineNumber; -}; - -class SecurityPolicyViolationEvent : public Event { -public: - static PassRefPtr<SecurityPolicyViolationEvent> create() + static Ref<SecurityPolicyViolationEvent> createForBindings() { - return adoptRef(new SecurityPolicyViolationEvent()); + return adoptRef(*new SecurityPolicyViolationEvent()); } - static PassRefPtr<SecurityPolicyViolationEvent> create(const AtomicString& type, const SecurityPolicyViolationEventInit& initializer) + struct Init : EventInit { + String documentURI; + String referrer; + String blockedURI; + String violatedDirective; + String effectiveDirective; + String originalPolicy; + String sourceFile; + unsigned short statusCode { 0 }; + int lineNumber { 0 }; + int columnNumber { 0 }; + }; + + static Ref<SecurityPolicyViolationEvent> create(const AtomicString& type, const Init& initializer, IsTrusted isTrusted = IsTrusted::No) { - return adoptRef(new SecurityPolicyViolationEvent(type, initializer)); + return adoptRef(*new SecurityPolicyViolationEvent(type, initializer, isTrusted)); } const String& documentURI() const { return m_documentURI; } @@ -66,17 +66,34 @@ public: const String& effectiveDirective() const { return m_effectiveDirective; } const String& originalPolicy() const { return m_originalPolicy; } const String& sourceFile() const { return m_sourceFile; } + unsigned short statusCode() const { return m_statusCode; } int lineNumber() const { return m_lineNumber; } + int columnNumber() const { return m_columnNumber; } - virtual EventInterface eventInterface() const { return SecurityPolicyViolationEventInterfaceType; } + EventInterface eventInterface() const final { return SecurityPolicyViolationEventInterfaceType; } private: SecurityPolicyViolationEvent() { } - SecurityPolicyViolationEvent(const AtomicString& type, const SecurityPolicyViolationEventInit& initializer) - : Event(type, initializer) + SecurityPolicyViolationEvent(const AtomicString& type, bool canBubble, bool cancelable, const String& documentURI, const String& referrer, const String& blockedURI, const String& violatedDirective, const String& effectiveDirective, const String& originalPolicy, const String& sourceFile, unsigned short statusCode, int lineNumber, int columnNumber) + : Event(type, canBubble, cancelable) + , m_documentURI(documentURI) + , m_referrer(referrer) + , m_blockedURI(blockedURI) + , m_violatedDirective(violatedDirective) + , m_effectiveDirective(effectiveDirective) + , m_originalPolicy(originalPolicy) + , m_sourceFile(sourceFile) + , m_statusCode(statusCode) + , m_lineNumber(lineNumber) + , m_columnNumber(columnNumber) + { + } + + SecurityPolicyViolationEvent(const AtomicString& type, const Init& initializer, IsTrusted isTrusted) + : Event(type, initializer, isTrusted) , m_documentURI(initializer.documentURI) , m_referrer(initializer.referrer) , m_blockedURI(initializer.blockedURI) @@ -84,7 +101,9 @@ private: , m_effectiveDirective(initializer.effectiveDirective) , m_originalPolicy(initializer.originalPolicy) , m_sourceFile(initializer.sourceFile) + , m_statusCode(initializer.statusCode) , m_lineNumber(initializer.lineNumber) + , m_columnNumber(initializer.columnNumber) { } @@ -95,11 +114,9 @@ private: String m_effectiveDirective; String m_originalPolicy; String m_sourceFile; + unsigned short m_statusCode; int m_lineNumber; + int m_columnNumber; }; } // namespace WebCore - -#endif // ENABLE(CSP_NEXT) - -#endif // SecurityPolicyViolationEvent_h diff --git a/Source/WebCore/dom/SecurityPolicyViolationEvent.idl b/Source/WebCore/dom/SecurityPolicyViolationEvent.idl index 435707fce..cab6c52ee 100644 --- a/Source/WebCore/dom/SecurityPolicyViolationEvent.idl +++ b/Source/WebCore/dom/SecurityPolicyViolationEvent.idl @@ -1,5 +1,6 @@ /* * Copyright (C) 2013 Google Inc. All rights reserved. + * Copyright (C) 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,15 +24,29 @@ */ [ - Conditional=CSP_NEXT, - ConstructorTemplate=Event, + Constructor(DOMString type, optional SecurityPolicyViolationEventInit eventInitDict), ] interface SecurityPolicyViolationEvent : Event { - [InitializedByEventConstructor] readonly attribute DOMString documentURI; - [InitializedByEventConstructor] readonly attribute DOMString referrer; - [InitializedByEventConstructor] readonly attribute DOMString blockedURI; - [InitializedByEventConstructor] readonly attribute DOMString violatedDirective; - [InitializedByEventConstructor] readonly attribute DOMString effectiveDirective; - [InitializedByEventConstructor] readonly attribute DOMString originalPolicy; - [InitializedByEventConstructor] readonly attribute DOMString sourceFile; - [InitializedByEventConstructor] readonly attribute long lineNumber; + readonly attribute USVString documentURI; + readonly attribute DOMString referrer; + readonly attribute USVString blockedURI; + readonly attribute DOMString violatedDirective; + readonly attribute DOMString effectiveDirective; + readonly attribute DOMString originalPolicy; + readonly attribute USVString sourceFile; + readonly attribute unsigned short statusCode; + readonly attribute long lineNumber; + readonly attribute long columnNumber; +}; + +dictionary SecurityPolicyViolationEventInit : EventInit { + USVString documentURI = ""; + DOMString referrer = ""; + USVString blockedURI = ""; + DOMString violatedDirective = ""; + DOMString effectiveDirective = ""; + DOMString originalPolicy = ""; + USVString sourceFile = ""; + unsigned short statusCode = 0; + long lineNumber = 0; + long columnNumber = 0; }; diff --git a/Source/WebCore/dom/SelectorQuery.cpp b/Source/WebCore/dom/SelectorQuery.cpp index c8f9dd8cc..f49bc30c5 100644 --- a/Source/WebCore/dom/SelectorQuery.cpp +++ b/Source/WebCore/dom/SelectorQuery.cpp @@ -1,5 +1,6 @@ /* - * Copyright (C) 2011, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2011, 2013, 2014, 2016 Apple Inc. All rights reserved. + * Copyright (C) 2014 Yusuke Suzuki <utatane.tea@gmail.com> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,64 +28,152 @@ #include "SelectorQuery.h" #include "CSSParser.h" -#include "ElementIterator.h" +#include "ElementDescendantIterator.h" +#include "ExceptionCode.h" +#include "HTMLNames.h" #include "SelectorChecker.h" -#include "SelectorCheckerFastPath.h" #include "StaticNodeList.h" #include "StyledElement.h" namespace WebCore { -void SelectorDataList::initialize(const CSSSelectorList& selectorList) +#if !ASSERT_DISABLED +static bool isSingleTagNameSelector(const CSSSelector& selector) +{ + return selector.isLastInTagHistory() && selector.match() == CSSSelector::Tag; +} + +static bool isSingleClassNameSelector(const CSSSelector& selector) { - ASSERT(m_selectors.isEmpty()); + return selector.isLastInTagHistory() && selector.match() == CSSSelector::Class; +} +#endif + +enum class IdMatchingType : uint8_t { + None, + Rightmost, + Filter +}; + +static bool canBeUsedForIdFastPath(const CSSSelector& selector) +{ + return selector.match() == CSSSelector::Id + || (selector.match() == CSSSelector::Exact && selector.attribute() == HTMLNames::idAttr && !selector.attributeValueMatchingIsCaseInsensitive()); +} + +static IdMatchingType findIdMatchingType(const CSSSelector& firstSelector) +{ + bool inRightmost = true; + for (const CSSSelector* selector = &firstSelector; selector; selector = selector->tagHistory()) { + if (canBeUsedForIdFastPath(*selector)) { + if (inRightmost) + return IdMatchingType::Rightmost; + return IdMatchingType::Filter; + } + if (selector->relation() != CSSSelector::Subselector) + inRightmost = false; + } + return IdMatchingType::None; +} +SelectorDataList::SelectorDataList(const CSSSelectorList& selectorList) +{ unsigned selectorCount = 0; for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector)) selectorCount++; m_selectors.reserveInitialCapacity(selectorCount); for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector)) - m_selectors.uncheckedAppend(SelectorData(selector, SelectorCheckerFastPath::canUse(selector))); + m_selectors.uncheckedAppend(SelectorData(selector)); + + if (selectorCount == 1) { + const CSSSelector& selector = *m_selectors.first().selector; + if (selector.isLastInTagHistory()) { + switch (selector.match()) { + case CSSSelector::Tag: + m_matchType = TagNameMatch; + break; + case CSSSelector::Class: + m_matchType = ClassNameMatch; + break; + default: + if (canBeUsedForIdFastPath(selector)) + m_matchType = RightMostWithIdMatch; + else + m_matchType = CompilableSingle; + break; + } + } else { + switch (findIdMatchingType(selector)) { + case IdMatchingType::None: + m_matchType = CompilableSingle; + break; + case IdMatchingType::Rightmost: + m_matchType = RightMostWithIdMatch; + break; + case IdMatchingType::Filter: + m_matchType = CompilableSingleWithRootFilter; + break; + } + } + } else + m_matchType = CompilableMultipleSelectorMatch; } inline bool SelectorDataList::selectorMatches(const SelectorData& selectorData, Element& element, const ContainerNode& rootNode) const { - if (selectorData.isFastCheckable && !element.isSVGElement()) { - SelectorCheckerFastPath selectorCheckerFastPath(selectorData.selector, &element); - if (!selectorCheckerFastPath.matchesRightmostSelector(SelectorChecker::VisitedMatchDisabled)) - return false; - return selectorCheckerFastPath.matches(); - } + SelectorChecker selectorChecker(element.document()); + SelectorChecker::CheckingContext selectorCheckingContext(SelectorChecker::Mode::QueryingRules); + selectorCheckingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode; + unsigned ignoredSpecificity; + return selectorChecker.match(*selectorData.selector, element, selectorCheckingContext, ignoredSpecificity); +} - SelectorChecker selectorChecker(element.document(), SelectorChecker::QueryingRules); - SelectorChecker::SelectorCheckingContext selectorCheckingContext(selectorData.selector, &element, SelectorChecker::VisitedMatchDisabled); +inline Element* SelectorDataList::selectorClosest(const SelectorData& selectorData, Element& element, const ContainerNode& rootNode) const +{ + SelectorChecker selectorChecker(element.document()); + SelectorChecker::CheckingContext selectorCheckingContext(SelectorChecker::Mode::QueryingRules); selectorCheckingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode; - PseudoId ignoreDynamicPseudo = NOPSEUDO; - return selectorChecker.match(selectorCheckingContext, ignoreDynamicPseudo); + unsigned ignoredSpecificity; + if (!selectorChecker.match(*selectorData.selector, element, selectorCheckingContext, ignoredSpecificity)) + return nullptr; + return &element; } bool SelectorDataList::matches(Element& targetElement) const { - unsigned selectorCount = m_selectors.size(); - for (unsigned i = 0; i < selectorCount; ++i) { - if (selectorMatches(m_selectors[i], targetElement, targetElement)) + for (auto& selctor : m_selectors) { + if (selectorMatches(selctor, targetElement, targetElement)) return true; } return false; } +Element* SelectorDataList::closest(Element& targetElement) const +{ + Element* currentNode = &targetElement; + do { + for (auto& selector : m_selectors) { + Element* candidateElement = selectorClosest(selector, *currentNode, targetElement); + if (candidateElement) + return candidateElement; + } + currentNode = currentNode->parentElement(); + } while (currentNode); + return nullptr; +} + struct AllElementExtractorSelectorQueryTrait { typedef Vector<Ref<Element>> OutputType; static const bool shouldOnlyMatchFirstElement = false; ALWAYS_INLINE static void appendOutputForElement(OutputType& output, Element* element) { ASSERT(element); output.append(*element); } }; -RefPtr<NodeList> SelectorDataList::queryAll(ContainerNode& rootNode) const +Ref<NodeList> SelectorDataList::queryAll(ContainerNode& rootNode) const { Vector<Ref<Element>> result; execute<AllElementExtractorSelectorQueryTrait>(rootNode, result); - return StaticElementList::adopt(result); + return StaticElementList::create(WTFMove(result)); } struct SingleElementExtractorSelectorQueryTrait { @@ -107,15 +196,15 @@ Element* SelectorDataList::queryFirst(ContainerNode& rootNode) const static const CSSSelector* selectorForIdLookup(const ContainerNode& rootNode, const CSSSelector& firstSelector) { - if (!rootNode.inDocument()) + if (!rootNode.isConnected()) return nullptr; if (rootNode.document().inQuirksMode()) return nullptr; for (const CSSSelector* selector = &firstSelector; selector; selector = selector->tagHistory()) { - if (selector->m_match == CSSSelector::Id) + if (canBeUsedForIdFastPath(*selector)) return selector; - if (selector->relation() != CSSSelector::SubSelector) + if (selector->relation() != CSSSelector::Subselector) break; } @@ -137,12 +226,10 @@ ALWAYS_INLINE void SelectorDataList::executeFastPathForIdSelector(const Containe if (UNLIKELY(rootNode.treeScope().containsMultipleElementsWithId(idToMatch))) { const Vector<Element*>* elements = rootNode.treeScope().getAllElementsById(idToMatch); ASSERT(elements); - size_t count = elements->size(); bool rootNodeIsTreeScopeRoot = isTreeScopeRoot(rootNode); - for (size_t i = 0; i < count; ++i) { - Element& element = *elements->at(i); - if ((rootNodeIsTreeScopeRoot || element.isDescendantOf(&rootNode)) && selectorMatches(selectorData, element, rootNode)) { - SelectorQueryTrait::appendOutputForElement(output, &element); + for (auto& element : *elements) { + if ((rootNodeIsTreeScopeRoot || element->isDescendantOf(rootNode)) && selectorMatches(selectorData, *element, rootNode)) { + SelectorQueryTrait::appendOutputForElement(output, element); if (SelectorQueryTrait::shouldOnlyMatchFirstElement) return; } @@ -151,25 +238,78 @@ ALWAYS_INLINE void SelectorDataList::executeFastPathForIdSelector(const Containe } Element* element = rootNode.treeScope().getElementById(idToMatch); - if (!element || !(isTreeScopeRoot(rootNode) || element->isDescendantOf(&rootNode))) + if (!element || !(isTreeScopeRoot(rootNode) || element->isDescendantOf(rootNode))) return; if (selectorMatches(selectorData, *element, rootNode)) SelectorQueryTrait::appendOutputForElement(output, element); } -static bool isSingleTagNameSelector(const CSSSelector& selector) +static ContainerNode& filterRootById(ContainerNode& rootNode, const CSSSelector& firstSelector) { - return selector.isLastInTagHistory() && selector.m_match == CSSSelector::Tag; + if (!rootNode.isConnected()) + return rootNode; + if (rootNode.document().inQuirksMode()) + return rootNode; + + // If there was an Id match in the rightmost Simple Selector, we should be in a RightMostWithIdMatch, not in filter. + // Thus we can skip the rightmost match. + const CSSSelector* selector = &firstSelector; + do { + ASSERT(!canBeUsedForIdFastPath(*selector)); + if (selector->relation() != CSSSelector::Subselector) + break; + selector = selector->tagHistory(); + } while (selector); + + bool inAdjacentChain = false; + for (; selector; selector = selector->tagHistory()) { + if (canBeUsedForIdFastPath(*selector)) { + const AtomicString& idToMatch = selector->value(); + if (ContainerNode* searchRoot = rootNode.treeScope().getElementById(idToMatch)) { + if (LIKELY(!rootNode.treeScope().containsMultipleElementsWithId(idToMatch))) { + if (inAdjacentChain) + searchRoot = searchRoot->parentNode(); + if (searchRoot && (isTreeScopeRoot(rootNode) || searchRoot == &rootNode || searchRoot->isDescendantOf(rootNode))) + return *searchRoot; + } + } + } + if (selector->relation() == CSSSelector::Subselector) + continue; + if (selector->relation() == CSSSelector::DirectAdjacent || selector->relation() == CSSSelector::IndirectAdjacent) + inAdjacentChain = true; + else + inAdjacentChain = false; + } + return rootNode; +} + +static ALWAYS_INLINE bool localNameMatches(const Element& element, const AtomicString& localName, const AtomicString& lowercaseLocalName) +{ + if (element.isHTMLElement() && element.document().isHTMLDocument()) + return element.localName() == lowercaseLocalName; + return element.localName() == localName; + } template <typename SelectorQueryTrait> -static inline void elementsForLocalName(const ContainerNode& rootNode, const AtomicString& localName, typename SelectorQueryTrait::OutputType& output) +static inline void elementsForLocalName(const ContainerNode& rootNode, const AtomicString& localName, const AtomicString& lowercaseLocalName, typename SelectorQueryTrait::OutputType& output) { - for (auto& element : descendantsOfType<Element>(const_cast<ContainerNode&>(rootNode))) { - if (element.tagQName().localName() == localName) { - SelectorQueryTrait::appendOutputForElement(output, &element); - if (SelectorQueryTrait::shouldOnlyMatchFirstElement) + if (localName == lowercaseLocalName) { + for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) { + if (element.tagQName().localName() == localName) { + SelectorQueryTrait::appendOutputForElement(output, &element); + if (SelectorQueryTrait::shouldOnlyMatchFirstElement) return; + } + } + } else { + for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) { + if (localNameMatches(element, localName, lowercaseLocalName)) { + SelectorQueryTrait::appendOutputForElement(output, &element); + if (SelectorQueryTrait::shouldOnlyMatchFirstElement) + return; + } } } } @@ -177,7 +317,7 @@ static inline void elementsForLocalName(const ContainerNode& rootNode, const Ato template <typename SelectorQueryTrait> static inline void anyElement(const ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) { - for (auto& element : descendantsOfType<Element>(const_cast<ContainerNode&>(rootNode))) { + for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) { SelectorQueryTrait::appendOutputForElement(output, &element); if (SelectorQueryTrait::shouldOnlyMatchFirstElement) return; @@ -193,20 +333,21 @@ ALWAYS_INLINE void SelectorDataList::executeSingleTagNameSelectorData(const Cont const QualifiedName& tagQualifiedName = selectorData.selector->tagQName(); const AtomicString& selectorLocalName = tagQualifiedName.localName(); + const AtomicString& selectorLowercaseLocalName = selectorData.selector->tagLowercaseLocalName(); const AtomicString& selectorNamespaceURI = tagQualifiedName.namespaceURI(); if (selectorNamespaceURI == starAtom) { if (selectorLocalName != starAtom) { // Common case: name defined, selectorNamespaceURI is a wildcard. - elementsForLocalName<SelectorQueryTrait>(rootNode, selectorLocalName, output); + elementsForLocalName<SelectorQueryTrait>(rootNode, selectorLocalName, selectorLowercaseLocalName, output); } else { // Other fairly common case: both are wildcards. anyElement<SelectorQueryTrait>(rootNode, output); } } else { // Fallback: NamespaceURI is set, selectorLocalName may be starAtom. - for (auto& element : descendantsOfType<Element>(const_cast<ContainerNode&>(rootNode))) { - if (element.namespaceURI() == selectorNamespaceURI && (selectorLocalName == starAtom || element.tagQName().localName() == selectorLocalName)) { + for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) { + if (element.namespaceURI() == selectorNamespaceURI && localNameMatches(element, selectorLocalName, selectorLowercaseLocalName)) { SelectorQueryTrait::appendOutputForElement(output, &element); if (SelectorQueryTrait::shouldOnlyMatchFirstElement) return; @@ -215,11 +356,6 @@ ALWAYS_INLINE void SelectorDataList::executeSingleTagNameSelectorData(const Cont } } -static bool isSingleClassNameSelector(const CSSSelector& selector) -{ - return selector.isLastInTagHistory() && selector.m_match == CSSSelector::Class; -} - template <typename SelectorQueryTrait> ALWAYS_INLINE void SelectorDataList::executeSingleClassNameSelectorData(const ContainerNode& rootNode, const SelectorData& selectorData, typename SelectorQueryTrait::OutputType& output) const { @@ -227,7 +363,7 @@ ALWAYS_INLINE void SelectorDataList::executeSingleClassNameSelectorData(const Co ASSERT(isSingleClassNameSelector(*selectorData.selector)); const AtomicString& className = selectorData.selector->value(); - for (auto& element : descendantsOfType<Element>(const_cast<ContainerNode&>(rootNode))) { + for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) { if (element.hasClass() && element.classNames().contains(className)) { SelectorQueryTrait::appendOutputForElement(output, &element); if (SelectorQueryTrait::shouldOnlyMatchFirstElement) @@ -237,11 +373,11 @@ ALWAYS_INLINE void SelectorDataList::executeSingleClassNameSelectorData(const Co } template <typename SelectorQueryTrait> -ALWAYS_INLINE void SelectorDataList::executeSingleSelectorData(const ContainerNode& rootNode, const SelectorData& selectorData, typename SelectorQueryTrait::OutputType& output) const +ALWAYS_INLINE void SelectorDataList::executeSingleSelectorData(const ContainerNode& rootNode, const ContainerNode& searchRootNode, const SelectorData& selectorData, typename SelectorQueryTrait::OutputType& output) const { ASSERT(m_selectors.size() == 1); - for (auto& element : descendantsOfType<Element>(const_cast<ContainerNode&>(rootNode))) { + for (auto& element : elementDescendants(const_cast<ContainerNode&>(searchRootNode))) { if (selectorMatches(selectorData, element, rootNode)) { SelectorQueryTrait::appendOutputForElement(output, &element); if (SelectorQueryTrait::shouldOnlyMatchFirstElement) @@ -253,10 +389,9 @@ ALWAYS_INLINE void SelectorDataList::executeSingleSelectorData(const ContainerNo template <typename SelectorQueryTrait> ALWAYS_INLINE void SelectorDataList::executeSingleMultiSelectorData(const ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const { - unsigned selectorCount = m_selectors.size(); - for (auto& element : descendantsOfType<Element>(const_cast<ContainerNode&>(rootNode))) { - for (unsigned i = 0; i < selectorCount; ++i) { - if (selectorMatches(m_selectors[i], element, rootNode)) { + for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) { + for (auto& selector : m_selectors) { + if (selectorMatches(selector, element, rootNode)) { SelectorQueryTrait::appendOutputForElement(output, &element); if (SelectorQueryTrait::shouldOnlyMatchFirstElement) return; @@ -268,9 +403,14 @@ ALWAYS_INLINE void SelectorDataList::executeSingleMultiSelectorData(const Contai #if ENABLE(CSS_SELECTOR_JIT) template <typename SelectorQueryTrait> -ALWAYS_INLINE void SelectorDataList::executeCompiledSimpleSelectorChecker(const ContainerNode& rootNode, SelectorCompiler::SimpleSelectorChecker selectorChecker, typename SelectorQueryTrait::OutputType& output) const +ALWAYS_INLINE void SelectorDataList::executeCompiledSimpleSelectorChecker(const ContainerNode& searchRootNode, SelectorCompiler::QuerySelectorSimpleSelectorChecker selectorChecker, typename SelectorQueryTrait::OutputType& output, const SelectorData& selectorData) const { - for (auto& element : descendantsOfType<Element>(const_cast<ContainerNode&>(rootNode))) { + for (auto& element : elementDescendants(const_cast<ContainerNode&>(searchRootNode))) { +#if CSS_SELECTOR_JIT_PROFILING + selectorData.compiledSelectorUsed(); +#else + UNUSED_PARAM(selectorData); +#endif if (selectorChecker(&element)) { SelectorQueryTrait::appendOutputForElement(output, &element); if (SelectorQueryTrait::shouldOnlyMatchFirstElement) @@ -280,98 +420,223 @@ ALWAYS_INLINE void SelectorDataList::executeCompiledSimpleSelectorChecker(const } template <typename SelectorQueryTrait> -ALWAYS_INLINE void SelectorDataList::executeCompiledSelectorCheckerWithContext(const ContainerNode& rootNode, SelectorCompiler::SelectorCheckerWithCheckingContext selectorChecker, const SelectorCompiler::CheckingContext& context, typename SelectorQueryTrait::OutputType& output) const +ALWAYS_INLINE void SelectorDataList::executeCompiledSelectorCheckerWithCheckingContext(const ContainerNode& rootNode, const ContainerNode& searchRootNode, SelectorCompiler::QuerySelectorSelectorCheckerWithCheckingContext selectorChecker, typename SelectorQueryTrait::OutputType& output, const SelectorData& selectorData) const { - for (auto& element : descendantsOfType<Element>(const_cast<ContainerNode&>(rootNode))) { - if (selectorChecker(&element, &context)) { + SelectorChecker::CheckingContext checkingContext(SelectorChecker::Mode::QueryingRules); + checkingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode; + + for (auto& element : elementDescendants(const_cast<ContainerNode&>(searchRootNode))) { +#if CSS_SELECTOR_JIT_PROFILING + selectorData.compiledSelectorUsed(); +#else + UNUSED_PARAM(selectorData); +#endif + if (selectorChecker(&element, &checkingContext)) { SelectorQueryTrait::appendOutputForElement(output, &element); if (SelectorQueryTrait::shouldOnlyMatchFirstElement) return; } } } + +template <typename SelectorQueryTrait> +ALWAYS_INLINE void SelectorDataList::executeCompiledSingleMultiSelectorData(const ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const +{ + SelectorChecker::CheckingContext checkingContext(SelectorChecker::Mode::QueryingRules); + checkingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode; + for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) { + for (auto& selector : m_selectors) { +#if CSS_SELECTOR_JIT_PROFILING + selector.compiledSelectorUsed(); +#endif + bool matched = false; + void* compiledSelectorChecker = selector.compiledSelectorCodeRef.code().executableAddress(); + if (selector.compilationStatus == SelectorCompilationStatus::SimpleSelectorChecker) { + auto selectorChecker = SelectorCompiler::querySelectorSimpleSelectorCheckerFunction(compiledSelectorChecker, selector.compilationStatus); + matched = selectorChecker(&element); + } else { + ASSERT(selector.compilationStatus == SelectorCompilationStatus::SelectorCheckerWithCheckingContext); + auto selectorChecker = SelectorCompiler::querySelectorSelectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, selector.compilationStatus); + matched = selectorChecker(&element, &checkingContext); + } + if (matched) { + SelectorQueryTrait::appendOutputForElement(output, &element); + if (SelectorQueryTrait::shouldOnlyMatchFirstElement) + return; + break; + } + } + } +} + +static bool isCompiledSelector(SelectorCompilationStatus compilationStatus) +{ + return compilationStatus == SelectorCompilationStatus::SimpleSelectorChecker || compilationStatus == SelectorCompilationStatus::SelectorCheckerWithCheckingContext; +} + +bool SelectorDataList::compileSelector(const SelectorData& selectorData, const ContainerNode& rootNode) +{ + if (selectorData.compilationStatus != SelectorCompilationStatus::NotCompiled) + return isCompiledSelector(selectorData.compilationStatus); + + JSC::VM& vm = rootNode.document().scriptExecutionContext()->vm(); + selectorData.compilationStatus = SelectorCompiler::compileSelector(selectorData.selector, &vm, SelectorCompiler::SelectorContext::QuerySelector, selectorData.compiledSelectorCodeRef); + return isCompiledSelector(selectorData.compilationStatus); +} + + #endif // ENABLE(CSS_SELECTOR_JIT) template <typename SelectorQueryTrait> ALWAYS_INLINE void SelectorDataList::execute(ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const { - if (m_selectors.size() == 1) { - const SelectorData& selectorData = m_selectors[0]; - if (const CSSSelector* idSelector = selectorForIdLookup(rootNode, *selectorData.selector)) - executeFastPathForIdSelector<SelectorQueryTrait>(rootNode, selectorData, idSelector, output); - else if (isSingleTagNameSelector(*selectorData.selector)) - executeSingleTagNameSelectorData<SelectorQueryTrait>(rootNode, selectorData, output); - else if (isSingleClassNameSelector(*selectorData.selector)) - executeSingleClassNameSelectorData<SelectorQueryTrait>(rootNode, selectorData, output); - else { + ContainerNode* searchRootNode = &rootNode; + switch (m_matchType) { + case RightMostWithIdMatch: + { + const SelectorData& selectorData = m_selectors.first(); + if (const CSSSelector* idSelector = selectorForIdLookup(*searchRootNode, *selectorData.selector)) { + executeFastPathForIdSelector<SelectorQueryTrait>(*searchRootNode, m_selectors.first(), idSelector, output); + break; + } #if ENABLE(CSS_SELECTOR_JIT) - void* compiledSelectorChecker = selectorData.compiledSelectorCodeRef.code().executableAddress(); - if (!compiledSelectorChecker && selectorData.compilationStatus == SelectorCompilationStatus::NotCompiled) { - JSC::VM* vm = rootNode.document().scriptExecutionContext()->vm(); - selectorData.compilationStatus = SelectorCompiler::compileSelector(selectorData.selector, vm, selectorData.compiledSelectorCodeRef); - } + if (compileSelector(selectorData, *searchRootNode)) + goto CompiledSingleCase; +#endif // ENABLE(CSS_SELECTOR_JIT) + goto SingleSelectorCase; + ASSERT_NOT_REACHED(); + } - if (compiledSelectorChecker) { - if (selectorData.compilationStatus == SelectorCompilationStatus::SimpleSelectorChecker) { - SelectorCompiler::SimpleSelectorChecker selectorChecker = SelectorCompiler::simpleSelectorCheckerFunction(compiledSelectorChecker, selectorData.compilationStatus); - executeCompiledSimpleSelectorChecker<SelectorQueryTrait>(rootNode, selectorChecker, output); - } else { - ASSERT(selectorData.compilationStatus == SelectorCompilationStatus::SelectorCheckerWithCheckingContext); - SelectorCompiler::SelectorCheckerWithCheckingContext selectorChecker = SelectorCompiler::selectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, selectorData.compilationStatus); - - SelectorCompiler::CheckingContext context; - context.elementStyle = nullptr; - context.resolvingMode = SelectorChecker::QueryingRules; - executeCompiledSelectorCheckerWithContext<SelectorQueryTrait>(rootNode, selectorChecker, context, output); - } - return; + case CompilableSingleWithRootFilter: + case CompilableSingle: + { +#if ENABLE(CSS_SELECTOR_JIT) + const SelectorData& selectorData = m_selectors.first(); + ASSERT(selectorData.compilationStatus == SelectorCompilationStatus::NotCompiled); + ASSERT(m_matchType == CompilableSingle || m_matchType == CompilableSingleWithRootFilter); + if (compileSelector(selectorData, *searchRootNode)) { + if (m_matchType == CompilableSingle) { + m_matchType = CompiledSingle; + goto CompiledSingleCase; } + ASSERT(m_matchType == CompilableSingleWithRootFilter); + m_matchType = CompiledSingleWithRootFilter; + goto CompiledSingleWithRootFilterCase; + } #endif // ENABLE(CSS_SELECTOR_JIT) + if (m_matchType == CompilableSingle) { + m_matchType = SingleSelector; + goto SingleSelectorCase; + } + ASSERT(m_matchType == CompilableSingleWithRootFilter); + m_matchType = SingleSelectorWithRootFilter; + goto SingleSelectorWithRootFilterCase; + ASSERT_NOT_REACHED(); + } - executeSingleSelectorData<SelectorQueryTrait>(rootNode, selectorData, output); +#if ENABLE(CSS_SELECTOR_JIT) + case CompiledSingleWithRootFilter: + CompiledSingleWithRootFilterCase: + searchRootNode = &filterRootById(*searchRootNode, *m_selectors.first().selector); + FALLTHROUGH; + case CompiledSingle: + { + CompiledSingleCase: + const SelectorData& selectorData = m_selectors.first(); + void* compiledSelectorChecker = selectorData.compiledSelectorCodeRef.code().executableAddress(); + if (selectorData.compilationStatus == SelectorCompilationStatus::SimpleSelectorChecker) { + SelectorCompiler::QuerySelectorSimpleSelectorChecker selectorChecker = SelectorCompiler::querySelectorSimpleSelectorCheckerFunction(compiledSelectorChecker, selectorData.compilationStatus); + executeCompiledSimpleSelectorChecker<SelectorQueryTrait>(*searchRootNode, selectorChecker, output, selectorData); + } else { + ASSERT(selectorData.compilationStatus == SelectorCompilationStatus::SelectorCheckerWithCheckingContext); + SelectorCompiler::QuerySelectorSelectorCheckerWithCheckingContext selectorChecker = SelectorCompiler::querySelectorSelectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, selectorData.compilationStatus); + executeCompiledSelectorCheckerWithCheckingContext<SelectorQueryTrait>(rootNode, *searchRootNode, selectorChecker, output, selectorData); } - return; + break; + } +#else + case CompiledSingleWithRootFilter: + case CompiledSingle: + ASSERT_NOT_REACHED(); +#if ASSERT_DISABLED + FALLTHROUGH; +#endif +#endif // ENABLE(CSS_SELECTOR_JIT) + + case SingleSelectorWithRootFilter: + SingleSelectorWithRootFilterCase: + searchRootNode = &filterRootById(*searchRootNode, *m_selectors.first().selector); + FALLTHROUGH; + case SingleSelector: + SingleSelectorCase: + executeSingleSelectorData<SelectorQueryTrait>(rootNode, *searchRootNode, m_selectors.first(), output); + break; + + case TagNameMatch: + executeSingleTagNameSelectorData<SelectorQueryTrait>(*searchRootNode, m_selectors.first(), output); + break; + case ClassNameMatch: + executeSingleClassNameSelectorData<SelectorQueryTrait>(*searchRootNode, m_selectors.first(), output); + break; + case CompilableMultipleSelectorMatch: +#if ENABLE(CSS_SELECTOR_JIT) + { + for (auto& selector : m_selectors) { + if (!compileSelector(selector, *searchRootNode)) { + m_matchType = MultipleSelectorMatch; + goto MultipleSelectorMatch; + } + } + m_matchType = CompiledMultipleSelectorMatch; + goto CompiledMultipleSelectorMatch; + } +#else + FALLTHROUGH; +#endif // ENABLE(CSS_SELECTOR_JIT) + case CompiledMultipleSelectorMatch: +#if ENABLE(CSS_SELECTOR_JIT) + CompiledMultipleSelectorMatch: + executeCompiledSingleMultiSelectorData<SelectorQueryTrait>(*searchRootNode, output); + break; +#else + FALLTHROUGH; +#endif // ENABLE(CSS_SELECTOR_JIT) + case MultipleSelectorMatch: +#if ENABLE(CSS_SELECTOR_JIT) + MultipleSelectorMatch: +#endif + executeSingleMultiSelectorData<SelectorQueryTrait>(*searchRootNode, output); + break; } - executeSingleMultiSelectorData<SelectorQueryTrait>(rootNode, output); } -SelectorQuery::SelectorQuery(const CSSSelectorList& selectorList) - : m_selectorList(selectorList) +SelectorQuery::SelectorQuery(CSSSelectorList&& selectorList) + : m_selectorList(WTFMove(selectorList)) + , m_selectors(m_selectorList) { - m_selectors.initialize(m_selectorList); } -SelectorQuery* SelectorQueryCache::add(const AtomicString& selectors, Document& document, ExceptionCode& ec) +ExceptionOr<SelectorQuery&> SelectorQueryCache::add(const String& selectors, Document& document) { auto it = m_entries.find(selectors); if (it != m_entries.end()) - return it->value.get(); + return *it->value; CSSParser parser(document); CSSSelectorList selectorList; parser.parseSelector(selectors, selectorList); - if (!selectorList.first() || selectorList.hasInvalidSelector()) { - ec = SYNTAX_ERR; - return nullptr; - } + if (!selectorList.first() || selectorList.hasInvalidSelector()) + return Exception { SYNTAX_ERR }; - // Throw a NAMESPACE_ERR if the selector includes any namespace prefixes. - if (selectorList.selectorsNeedNamespaceResolution()) { - ec = NAMESPACE_ERR; - return nullptr; - } + if (selectorList.selectorsNeedNamespaceResolution()) + return Exception { SYNTAX_ERR }; const int maximumSelectorQueryCacheSize = 256; if (m_entries.size() == maximumSelectorQueryCacheSize) m_entries.remove(m_entries.begin()); - - return m_entries.add(selectors, std::make_unique<SelectorQuery>(selectorList)).iterator->value.get(); -} -void SelectorQueryCache::invalidate() -{ - m_entries.clear(); + return *m_entries.add(selectors, std::make_unique<SelectorQuery>(WTFMove(selectorList))).iterator->value; } } diff --git a/Source/WebCore/dom/SelectorQuery.h b/Source/WebCore/dom/SelectorQuery.h index aab4276dd..5e3cc1f5c 100644 --- a/Source/WebCore/dom/SelectorQuery.h +++ b/Source/WebCore/dom/SelectorQuery.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2011, 2013, 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,60 +23,89 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SelectorQuery_h -#define SelectorQuery_h +#pragma once #include "CSSSelectorList.h" +#include "ExceptionOr.h" #include "NodeList.h" #include "SelectorCompiler.h" #include <wtf/HashMap.h> #include <wtf/Vector.h> #include <wtf/text/AtomicStringHash.h> +#include <wtf/text/CString.h> namespace WebCore { -typedef int ExceptionCode; - class CSSSelector; class ContainerNode; class Document; class Element; -class Node; -class NodeList; class SelectorDataList { public: - void initialize(const CSSSelectorList&); + explicit SelectorDataList(const CSSSelectorList&); bool matches(Element&) const; - RefPtr<NodeList> queryAll(ContainerNode& rootNode) const; + Element* closest(Element&) const; + Ref<NodeList> queryAll(ContainerNode& rootNode) const; Element* queryFirst(ContainerNode& rootNode) const; private: struct SelectorData { - SelectorData(const CSSSelector* selector, bool isFastCheckable) : selector(selector), isFastCheckable(isFastCheckable) { } - const CSSSelector* selector; - bool isFastCheckable; + SelectorData(const CSSSelector* selector) + : selector(selector) +#if ENABLE(CSS_SELECTOR_JIT) && CSS_SELECTOR_JIT_PROFILING + , m_compiledSelectorUseCount(0) +#endif + { + } + const CSSSelector* selector; #if ENABLE(CSS_SELECTOR_JIT) - mutable SelectorCompilationStatus compilationStatus; mutable JSC::MacroAssemblerCodeRef compiledSelectorCodeRef; + mutable SelectorCompilationStatus compilationStatus; +#if CSS_SELECTOR_JIT_PROFILING + ~SelectorData() + { + if (compiledSelectorCodeRef.code().executableAddress()) + dataLogF("SelectorData compiled selector %d \"%s\"\n", m_compiledSelectorUseCount, selector->selectorText().utf8().data()); + } + mutable unsigned m_compiledSelectorUseCount; + void compiledSelectorUsed() const { m_compiledSelectorUseCount++; } +#endif #endif // ENABLE(CSS_SELECTOR_JIT) }; bool selectorMatches(const SelectorData&, Element&, const ContainerNode& rootNode) const; + Element* selectorClosest(const SelectorData&, Element&, const ContainerNode& rootNode) const; template <typename SelectorQueryTrait> void execute(ContainerNode& rootNode, typename SelectorQueryTrait::OutputType&) const; template <typename SelectorQueryTrait> void executeFastPathForIdSelector(const ContainerNode& rootNode, const SelectorData&, const CSSSelector* idSelector, typename SelectorQueryTrait::OutputType&) const; template <typename SelectorQueryTrait> void executeSingleTagNameSelectorData(const ContainerNode& rootNode, const SelectorData&, typename SelectorQueryTrait::OutputType&) const; template <typename SelectorQueryTrait> void executeSingleClassNameSelectorData(const ContainerNode& rootNode, const SelectorData&, typename SelectorQueryTrait::OutputType&) const; - template <typename SelectorQueryTrait> void executeSingleSelectorData(const ContainerNode& rootNode, const SelectorData&, typename SelectorQueryTrait::OutputType&) const; + template <typename SelectorQueryTrait> void executeSingleSelectorData(const ContainerNode& rootNode, const ContainerNode& searchRootNode, const SelectorData&, typename SelectorQueryTrait::OutputType&) const; template <typename SelectorQueryTrait> void executeSingleMultiSelectorData(const ContainerNode& rootNode, typename SelectorQueryTrait::OutputType&) const; #if ENABLE(CSS_SELECTOR_JIT) - template <typename SelectorQueryTrait> void executeCompiledSimpleSelectorChecker(const ContainerNode& rootNode, SelectorCompiler::SimpleSelectorChecker, typename SelectorQueryTrait::OutputType&) const; - template <typename SelectorQueryTrait> void executeCompiledSelectorCheckerWithContext(const ContainerNode& rootNode, SelectorCompiler::SelectorCheckerWithCheckingContext, const SelectorCompiler::CheckingContext&, typename SelectorQueryTrait::OutputType&) const; + template <typename SelectorQueryTrait> void executeCompiledSimpleSelectorChecker(const ContainerNode& searchRootNode, SelectorCompiler::QuerySelectorSimpleSelectorChecker, typename SelectorQueryTrait::OutputType&, const SelectorData&) const; + template <typename SelectorQueryTrait> void executeCompiledSelectorCheckerWithCheckingContext(const ContainerNode& rootNode, const ContainerNode& searchRootNode, SelectorCompiler::QuerySelectorSelectorCheckerWithCheckingContext, typename SelectorQueryTrait::OutputType&, const SelectorData&) const; + template <typename SelectorQueryTrait> void executeCompiledSingleMultiSelectorData(const ContainerNode& rootNode, typename SelectorQueryTrait::OutputType&) const; + static bool compileSelector(const SelectorData&, const ContainerNode& rootNode); #endif // ENABLE(CSS_SELECTOR_JIT) Vector<SelectorData> m_selectors; + mutable enum MatchType { + CompilableSingle, + CompilableSingleWithRootFilter, + CompilableMultipleSelectorMatch, + CompiledSingle, + CompiledSingleWithRootFilter, + CompiledMultipleSelectorMatch, + SingleSelector, + SingleSelectorWithRootFilter, + RightMostWithIdMatch, + TagNameMatch, + ClassNameMatch, + MultipleSelectorMatch, + } m_matchType; }; class SelectorQuery { @@ -84,25 +113,23 @@ class SelectorQuery { WTF_MAKE_FAST_ALLOCATED; public: - explicit SelectorQuery(const CSSSelectorList&); + explicit SelectorQuery(CSSSelectorList&&); bool matches(Element&) const; - RefPtr<NodeList> queryAll(ContainerNode& rootNode) const; + Element* closest(Element&) const; + Ref<NodeList> queryAll(ContainerNode& rootNode) const; Element* queryFirst(ContainerNode& rootNode) const; private: - SelectorDataList m_selectors; CSSSelectorList m_selectorList; + SelectorDataList m_selectors; }; class SelectorQueryCache { WTF_MAKE_FAST_ALLOCATED; - public: - SelectorQuery* add(const AtomicString&, Document&, ExceptionCode&); - void invalidate(); - + ExceptionOr<SelectorQuery&> add(const String&, Document&); private: - HashMap<AtomicString, std::unique_ptr<SelectorQuery>> m_entries; + HashMap<String, std::unique_ptr<SelectorQuery>> m_entries; }; inline bool SelectorQuery::matches(Element& element) const @@ -110,7 +137,12 @@ inline bool SelectorQuery::matches(Element& element) const return m_selectors.matches(element); } -inline RefPtr<NodeList> SelectorQuery::queryAll(ContainerNode& rootNode) const +inline Element* SelectorQuery::closest(Element& element) const +{ + return m_selectors.closest(element); +} + +inline Ref<NodeList> SelectorQuery::queryAll(ContainerNode& rootNode) const { return m_selectors.queryAll(rootNode); } @@ -120,6 +152,4 @@ inline Element* SelectorQuery::queryFirst(ContainerNode& rootNode) const return m_selectors.queryFirst(rootNode); } -} - -#endif +} // namespace WebCore diff --git a/Source/WebCore/dom/ShadowRoot.cpp b/Source/WebCore/dom/ShadowRoot.cpp index 8115139aa..f54fd74b2 100644 --- a/Source/WebCore/dom/ShadowRoot.cpp +++ b/Source/WebCore/dom/ShadowRoot.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -27,68 +28,94 @@ #include "config.h" #include "ShadowRoot.h" +#include "CSSStyleSheet.h" #include "ElementTraversal.h" -#include "HistogramSupport.h" -#include "InsertionPoint.h" +#include "ExceptionCode.h" +#include "HTMLSlotElement.h" #include "RenderElement.h" #include "RuntimeEnabledFeatures.h" +#include "SlotAssignment.h" #include "StyleResolver.h" +#include "StyleScope.h" #include "markup.h" namespace WebCore { struct SameSizeAsShadowRoot : public DocumentFragment, public TreeScope { unsigned countersAndFlags[1]; - ContentDistributor distributor; + void* styleScope; void* host; + void* slotAssignment; }; COMPILE_ASSERT(sizeof(ShadowRoot) == sizeof(SameSizeAsShadowRoot), shadowroot_should_stay_small); -enum ShadowRootUsageOriginType { - ShadowRootUsageOriginWeb = 0, - ShadowRootUsageOriginNotWeb, - ShadowRootUsageOriginMax -}; - -ShadowRoot::ShadowRoot(Document& document, ShadowRootType type) - : DocumentFragment(0, CreateShadowRoot) - , TreeScope(this, &document) - , m_applyAuthorStyles(false) - , m_resetStyleInheritance(false) +ShadowRoot::ShadowRoot(Document& document, ShadowRootMode type) + : DocumentFragment(document, CreateShadowRoot) + , TreeScope(*this, document) , m_type(type) - , m_hostElement(0) + , m_styleScope(std::make_unique<Style::Scope>(*this)) +{ +} + + +ShadowRoot::ShadowRoot(Document& document, std::unique_ptr<SlotAssignment>&& slotAssignment) + : DocumentFragment(document, CreateShadowRoot) + , TreeScope(*this, document) + , m_type(ShadowRootMode::UserAgent) + , m_styleScope(std::make_unique<Style::Scope>(*this)) + , m_slotAssignment(WTFMove(slotAssignment)) { } + ShadowRoot::~ShadowRoot() { + if (isConnected()) + document().didRemoveInDocumentShadowRoot(*this); + // We cannot let ContainerNode destructor call willBeDeletedFrom() // for this ShadowRoot instance because TreeScope destructor // clears Node::m_treeScope thus ContainerNode is no longer able // to access it Document reference after that. - willBeDeletedFrom(documentInternal()); + willBeDeletedFrom(document()); // We must remove all of our children first before the TreeScope destructor // runs so we don't go through TreeScopeAdopter for each child with a // destructed tree scope in each descendant. removeDetachedChildren(); +} - // We must call clearRareData() here since a ShadowRoot class inherits TreeScope - // as well as Node. See a comment on TreeScope.h for the reason. - if (hasRareData()) - clearRareData(); +Node::InsertionNotificationRequest ShadowRoot::insertedInto(ContainerNode& insertionPoint) +{ + bool wasInDocument = isConnected(); + DocumentFragment::insertedInto(insertionPoint); + if (insertionPoint.isConnected() && !wasInDocument) + document().didInsertInDocumentShadowRoot(*this); + return InsertionDone; } -void ShadowRoot::dropChildren() +void ShadowRoot::removedFrom(ContainerNode& insertionPoint) { - removeDetachedChildren(); + DocumentFragment::removedFrom(insertionPoint); + if (insertionPoint.isConnected() && !isConnected()) + document().didRemoveInDocumentShadowRoot(*this); } -PassRefPtr<Node> ShadowRoot::cloneNode(bool, ExceptionCode& ec) +void ShadowRoot::didMoveToNewDocument(Document& oldDocument) { - ec = DATA_CLONE_ERR; - return 0; + ASSERT(&document() != &oldDocument); + ASSERT(&m_styleScope->document() == &oldDocument); + + // Style scopes are document specific. + m_styleScope = std::make_unique<Style::Scope>(*this); + + DocumentFragment::didMoveToNewDocument(oldDocument); +} + +Style::Scope& ShadowRoot::styleScope() +{ + return *m_styleScope; } String ShadowRoot::innerHTML() const @@ -96,15 +123,14 @@ String ShadowRoot::innerHTML() const return createMarkup(*this, ChildrenOnly); } -void ShadowRoot::setInnerHTML(const String& markup, ExceptionCode& ec) +ExceptionOr<void> ShadowRoot::setInnerHTML(const String& markup) { - if (isOrphan()) { - ec = INVALID_ACCESS_ERR; - return; - } - - if (RefPtr<DocumentFragment> fragment = createFragmentForInnerOuterHTML(markup, hostElement(), AllowScriptingContent, ec)) - replaceChildrenWithFragment(*this, fragment.release(), ec); + if (isOrphan()) + return Exception { INVALID_ACCESS_ERR }; + auto fragment = createFragmentForInnerOuterHTML(*host(), markup, AllowScriptingContent); + if (fragment.hasException()) + return fragment.releaseException(); + return replaceChildrenWithFragment(*this, fragment.releaseReturnValue()); } bool ShadowRoot::childTypeAllowed(NodeType type) const @@ -115,50 +141,68 @@ bool ShadowRoot::childTypeAllowed(NodeType type) const case COMMENT_NODE: case TEXT_NODE: case CDATA_SECTION_NODE: - case ENTITY_REFERENCE_NODE: return true; default: return false; } } -void ShadowRoot::setApplyAuthorStyles(bool value) +void ShadowRoot::setResetStyleInheritance(bool value) { - if (isOrphan()) - return; + // If this was ever changed after initialization, child styles would need to be invalidated here. + m_resetStyleInheritance = value; +} - if (m_applyAuthorStyles != value) { - m_applyAuthorStyles = value; - hostElement()->setNeedsStyleRecalc(); - } +Ref<Node> ShadowRoot::cloneNodeInternal(Document&, CloningOperation) +{ + RELEASE_ASSERT_NOT_REACHED(); + return *static_cast<Node*>(nullptr); // ShadowRoots should never be cloned. } -void ShadowRoot::setResetStyleInheritance(bool value) +void ShadowRoot::removeAllEventListeners() { - if (isOrphan()) - return; + DocumentFragment::removeAllEventListeners(); + for (Node* node = firstChild(); node; node = NodeTraversal::next(*node)) + node->removeAllEventListeners(); +} - if (value != m_resetStyleInheritance) { - m_resetStyleInheritance = value; - if (hostElement()) - setNeedsStyleRecalc(); - } + +HTMLSlotElement* ShadowRoot::findAssignedSlot(const Node& node) +{ + ASSERT(node.parentNode() == host()); + if (!m_slotAssignment) + return nullptr; + return m_slotAssignment->findAssignedSlot(node, *this); } -void ShadowRoot::childrenChanged(const ChildChange& change) +void ShadowRoot::addSlotElementByName(const AtomicString& name, HTMLSlotElement& slot) { - if (isOrphan()) - return; + if (!m_slotAssignment) + m_slotAssignment = std::make_unique<SlotAssignment>(); - ContainerNode::childrenChanged(change); - invalidateDistribution(); + return m_slotAssignment->addSlotElementByName(name, slot, *this); } -void ShadowRoot::removeAllEventListeners() +void ShadowRoot::removeSlotElementByName(const AtomicString& name, HTMLSlotElement& slot) { - DocumentFragment::removeAllEventListeners(); - for (Node* node = firstChild(); node; node = NodeTraversal::next(node)) - node->removeAllEventListeners(); + return m_slotAssignment->removeSlotElementByName(name, slot, *this); +} + +const Vector<Node*>* ShadowRoot::assignedNodesForSlot(const HTMLSlotElement& slot) +{ + if (!m_slotAssignment) + return nullptr; + return m_slotAssignment->assignedNodesForSlot(slot, *this); +} + +Vector<ShadowRoot*> assignedShadowRootsIfSlotted(const Node& node) +{ + Vector<ShadowRoot*> result; + for (auto* slot = node.assignedSlot(); slot; slot = slot->assignedSlot()) { + ASSERT(slot->containingShadowRoot()); + result.append(slot->containingShadowRoot()); + } + return result; } } diff --git a/Source/WebCore/dom/ShadowRoot.h b/Source/WebCore/dom/ShadowRoot.h index eb45ab8f3..86532e1bc 100644 --- a/Source/WebCore/dom/ShadowRoot.h +++ b/Source/WebCore/dom/ShadowRoot.h @@ -24,109 +24,107 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ShadowRoot_h -#define ShadowRoot_h +#pragma once -#include "ContainerNode.h" -#include "ContentDistributor.h" #include "Document.h" #include "DocumentFragment.h" #include "Element.h" -#include "ExceptionCode.h" -#include "TreeScope.h" +#include "ShadowRootMode.h" namespace WebCore { +class HTMLSlotElement; +class SlotAssignment; + class ShadowRoot final : public DocumentFragment, public TreeScope { public: - // FIXME: We will support multiple shadow subtrees, however current implementation does not work well - // if a shadow root is dynamically created. So we prohibit multiple shadow subtrees - // in several elements for a while. - // See https://bugs.webkit.org/show_bug.cgi?id=77503 and related bugs. - enum ShadowRootType { - UserAgentShadowRoot = 0, - AuthorShadowRoot - }; - - static PassRefPtr<ShadowRoot> create(Document& document, ShadowRootType type) + static Ref<ShadowRoot> create(Document& document, ShadowRootMode type) { - return adoptRef(new ShadowRoot(document, type)); + return adoptRef(*new ShadowRoot(document, type)); + } + + static Ref<ShadowRoot> create(Document& document, std::unique_ptr<SlotAssignment>&& assignment) + { + return adoptRef(*new ShadowRoot(document, WTFMove(assignment))); } virtual ~ShadowRoot(); - virtual bool applyAuthorStyles() const override { return m_applyAuthorStyles; } - void setApplyAuthorStyles(bool); + using TreeScope::rootNode; + + Style::Scope& styleScope(); + bool resetStyleInheritance() const { return m_resetStyleInheritance; } void setResetStyleInheritance(bool); - Element* hostElement() const { return m_hostElement; } - void setHostElement(Element* hostElement) { m_hostElement = hostElement; } + Element* host() const { return m_host; } + void setHost(Element* host) { m_host = host; } String innerHTML() const; - void setInnerHTML(const String&, ExceptionCode&); + ExceptionOr<void> setInnerHTML(const String&); Element* activeElement() const; - ShadowRootType type() const { return static_cast<ShadowRootType>(m_type); } + ShadowRootMode mode() const { return m_type; } - PassRefPtr<Node> cloneNode(bool, ExceptionCode&); + void removeAllEventListeners() override; - ContentDistributor& distributor() { return m_distributor; } - void invalidateDistribution() { m_distributor.invalidateDistribution(hostElement()); } + HTMLSlotElement* findAssignedSlot(const Node&); - virtual void removeAllEventListeners() override; + void addSlotElementByName(const AtomicString&, HTMLSlotElement&); + void removeSlotElementByName(const AtomicString&, HTMLSlotElement&); -private: - ShadowRoot(Document&, ShadowRootType); + void didRemoveAllChildrenOfShadowHost(); + void didChangeDefaultSlot(); + void hostChildElementDidChange(const Element&); + void hostChildElementDidChangeSlotAttribute(Element&, const AtomicString& oldValue, const AtomicString& newValue); + + const Vector<Node*>* assignedNodesForSlot(const HTMLSlotElement&); - virtual void dropChildren() override; - virtual bool childTypeAllowed(NodeType) const override; - virtual void childrenChanged(const ChildChange&) override; +protected: + ShadowRoot(Document&, ShadowRootMode); - // ShadowRoots should never be cloned. - virtual PassRefPtr<Node> cloneNode(bool) override { return 0; } + ShadowRoot(Document&, std::unique_ptr<SlotAssignment>&&); // FIXME: This shouldn't happen. https://bugs.webkit.org/show_bug.cgi?id=88834 - bool isOrphan() const { return !hostElement(); } + bool isOrphan() const { return !m_host; } - unsigned m_applyAuthorStyles : 1; - unsigned m_resetStyleInheritance : 1; - unsigned m_type : 1; +private: + bool childTypeAllowed(NodeType) const override; - Element* m_hostElement; + Ref<Node> cloneNodeInternal(Document&, CloningOperation) override; - ContentDistributor m_distributor; -}; + Node::InsertionNotificationRequest insertedInto(ContainerNode& insertionPoint) override; + void removedFrom(ContainerNode& insertionPoint) override; + void didMoveToNewDocument(Document& oldDocument) override; -inline Element* ShadowRoot::activeElement() const -{ - return treeScope().focusedElement(); -} + bool m_resetStyleInheritance { false }; + ShadowRootMode m_type { ShadowRootMode::UserAgent }; -inline const ShadowRoot* toShadowRoot(const Node* node) -{ - ASSERT_WITH_SECURITY_IMPLICATION(!node || node->isShadowRoot()); - return static_cast<const ShadowRoot*>(node); -} + Element* m_host { nullptr }; -inline ShadowRoot* toShadowRoot(Node* node) + std::unique_ptr<Style::Scope> m_styleScope; + + std::unique_ptr<SlotAssignment> m_slotAssignment; +}; + +inline Element* ShadowRoot::activeElement() const { - return const_cast<ShadowRoot*>(toShadowRoot(static_cast<const Node*>(node))); + return treeScope().focusedElementInScope(); } inline ShadowRoot* Node::shadowRoot() const { - if (!isElementNode()) - return 0; - return toElement(this)->shadowRoot(); + if (!is<Element>(*this)) + return nullptr; + return downcast<Element>(*this).shadowRoot(); } inline ContainerNode* Node::parentOrShadowHostNode() const { ASSERT(isMainThreadOrGCThread()); - if (isShadowRoot()) - return toShadowRoot(this)->hostElement(); + if (is<ShadowRoot>(*this)) + return downcast<ShadowRoot>(*this).host(); return parentNode(); } @@ -135,6 +133,10 @@ inline bool hasShadowRootParent(const Node& node) return node.parentNode() && node.parentNode()->isShadowRoot(); } -} // namespace +Vector<ShadowRoot*> assignedShadowRootsIfSlotted(const Node&); + +} // namespace WebCore -#endif +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ShadowRoot) + static bool isType(const WebCore::Node& node) { return node.isShadowRoot(); } +SPECIALIZE_TYPE_TRAITS_END() diff --git a/Source/WebCore/dom/ShadowRoot.idl b/Source/WebCore/dom/ShadowRoot.idl index 07a3e41a2..a6d4a77cb 100644 --- a/Source/WebCore/dom/ShadowRoot.idl +++ b/Source/WebCore/dom/ShadowRoot.idl @@ -1,50 +1,36 @@ /* - * Copyright (C) 2012 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ +* Copyright (C) 2015 Apple Inc. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ [ - Conditional=SHADOW_DOM, - JSGenerateToJSObject, - InterfaceName=WebKitShadowRoot, + EnabledAtRuntime=ShadowDOM, + JSGenerateToJSObject ] interface ShadowRoot : DocumentFragment { - readonly attribute Element activeElement; - attribute boolean applyAuthorStyles; - attribute boolean resetStyleInheritance; + readonly attribute ShadowRootMode mode; + readonly attribute Element host; - [TreatNullAs=NullString, SetterRaisesException] attribute DOMString innerHTML; - -#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT - [RaisesException] Node cloneNode([Default=Undefined] optional boolean deep); -#endif - DOMSelection getSelection(); - Element getElementById([Default=Undefined] optional DOMString elementId); - NodeList getElementsByClassName([Default=Undefined] optional DOMString className); - NodeList getElementsByTagName([Default=Undefined] optional DOMString tagName); - NodeList getElementsByTagNameNS([TreatNullAs=NullString,Default=Undefined] optional DOMString namespaceURI, - [Default=Undefined] optional DOMString localName); - - Element elementFromPoint([Default=Undefined] optional long x, - [Default=Undefined] optional long y); + [CEReactions, SetterMayThrowException, TreatNullAs=EmptyString] attribute DOMString innerHTML; }; + +ShadowRoot implements DocumentOrShadowRoot; diff --git a/Source/WebCore/dom/ShadowRootMode.h b/Source/WebCore/dom/ShadowRootMode.h new file mode 100644 index 000000000..004a96f63 --- /dev/null +++ b/Source/WebCore/dom/ShadowRootMode.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +namespace WebCore { + +enum class ShadowRootMode : uint8_t { + UserAgent, + Closed, + Open +}; + +} diff --git a/Source/WebCore/dom/ShadowRootMode.idl b/Source/WebCore/dom/ShadowRootMode.idl new file mode 100644 index 000000000..7e38ecadf --- /dev/null +++ b/Source/WebCore/dom/ShadowRootMode.idl @@ -0,0 +1,31 @@ +/* +* Copyright (C) 2016 Apple Inc. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// "user-agent" is a WebKit extension that is not exposed to the Web. +enum ShadowRootMode { + "user-agent", + "closed", + "open" +}; diff --git a/Source/WebCore/dom/SimulatedClick.cpp b/Source/WebCore/dom/SimulatedClick.cpp new file mode 100644 index 000000000..427ac5256 --- /dev/null +++ b/Source/WebCore/dom/SimulatedClick.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "SimulatedClick.h" + +#include "DataTransfer.h" +#include "Element.h" +#include "EventDispatcher.h" +#include "EventNames.h" +#include "MouseEvent.h" +#include <wtf/CurrentTime.h> +#include <wtf/NeverDestroyed.h> + +namespace WebCore { + +class SimulatedMouseEvent final : public MouseEvent { +public: + static Ref<SimulatedMouseEvent> create(const AtomicString& eventType, DOMWindow* view, RefPtr<Event>&& underlyingEvent, Element& target, SimulatedClickSource source) + { + return adoptRef(*new SimulatedMouseEvent(eventType, view, WTFMove(underlyingEvent), target, source)); + } + +private: + SimulatedMouseEvent(const AtomicString& eventType, DOMWindow* view, RefPtr<Event>&& underlyingEvent, Element& target, SimulatedClickSource source) + : MouseEvent(eventType, true, true, underlyingEvent ? underlyingEvent->timeStamp() : currentTime(), view, 0, { }, { }, +#if ENABLE(POINTER_LOCK) + { }, +#endif + false, false, false, false, 0, 0, 0, 0, 0, true) + { + if (source == SimulatedClickSource::Bindings) + setUntrusted(); + + if (UIEventWithKeyState* keyStateEvent = findEventWithKeyState(underlyingEvent.get())) { + m_ctrlKey = keyStateEvent->ctrlKey(); + m_altKey = keyStateEvent->altKey(); + m_shiftKey = keyStateEvent->shiftKey(); + m_metaKey = keyStateEvent->metaKey(); + } + setUnderlyingEvent(underlyingEvent.get()); + + if (is<MouseEvent>(this->underlyingEvent())) { + MouseEvent& mouseEvent = downcast<MouseEvent>(*this->underlyingEvent()); + m_screenLocation = mouseEvent.screenLocation(); + initCoordinates(mouseEvent.clientLocation()); + } else if (source == SimulatedClickSource::UserAgent) { + // If there is no underlying event, we only populate the coordinates for events coming + // from the user agent (e.g. accessibility). For those coming from JavaScript (e.g. + // (element.click()), the coordinates will be 0, similarly to Firefox and Chrome. + // Note that the call to screenRect() causes a synchronous IPC with the UI process. + m_screenLocation = target.screenRect().center(); + initCoordinates(LayoutPoint(target.clientRect().center())); + } + } + +}; + +static void simulateMouseEvent(const AtomicString& eventType, Element& element, Event* underlyingEvent, SimulatedClickSource source) +{ + auto event = SimulatedMouseEvent::create(eventType, element.document().defaultView(), underlyingEvent, element, source); + EventDispatcher::dispatchEvent(element, event); +} + +void simulateClick(Element& element, Event* underlyingEvent, SimulatedClickMouseEventOptions mouseEventOptions, SimulatedClickVisualOptions visualOptions, SimulatedClickSource creationOptions) +{ + if (element.isDisabledFormControl()) + return; + + static NeverDestroyed<HashSet<Element*>> elementsDispatchingSimulatedClicks; + if (!elementsDispatchingSimulatedClicks.get().add(&element).isNewEntry) + return; + + if (mouseEventOptions == SendMouseOverUpDownEvents) + simulateMouseEvent(eventNames().mouseoverEvent, element, underlyingEvent, creationOptions); + + if (mouseEventOptions != SendNoEvents) + simulateMouseEvent(eventNames().mousedownEvent, element, underlyingEvent, creationOptions); + element.setActive(true, visualOptions == ShowPressedLook); + if (mouseEventOptions != SendNoEvents) + simulateMouseEvent(eventNames().mouseupEvent, element, underlyingEvent, creationOptions); + element.setActive(false); + + simulateMouseEvent(eventNames().clickEvent, element, underlyingEvent, creationOptions); + + elementsDispatchingSimulatedClicks.get().remove(&element); +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/SimulatedClick.h b/Source/WebCore/dom/SimulatedClick.h new file mode 100644 index 000000000..ae6d5d68c --- /dev/null +++ b/Source/WebCore/dom/SimulatedClick.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "SimulatedClickOptions.h" + +namespace WebCore { + +class Element; +class Event; + +enum class SimulatedClickSource { + Bindings, + UserAgent +}; + +void simulateClick(Element&, Event* underlyingEvent, SimulatedClickMouseEventOptions, SimulatedClickVisualOptions, SimulatedClickSource); + +} // namespace WebCore diff --git a/Source/WebCore/dom/SimulatedClickOptions.h b/Source/WebCore/dom/SimulatedClickOptions.h index a49ed7496..3cb53976e 100644 --- a/Source/WebCore/dom/SimulatedClickOptions.h +++ b/Source/WebCore/dom/SimulatedClickOptions.h @@ -18,8 +18,7 @@ * */ -#ifndef SimulatedClickOptions_h -#define SimulatedClickOptions_h +#pragma once namespace WebCore { @@ -35,5 +34,3 @@ enum SimulatedClickVisualOptions { }; } // namespace WebCore - -#endif // SimulatedClickOptions_h diff --git a/Source/WebCore/dom/SlotAssignment.cpp b/Source/WebCore/dom/SlotAssignment.cpp new file mode 100644 index 000000000..956407a06 --- /dev/null +++ b/Source/WebCore/dom/SlotAssignment.cpp @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "SlotAssignment.h" + + +#include "HTMLSlotElement.h" +#include "ShadowRoot.h" +#include "TypedElementDescendantIterator.h" + +namespace WebCore { + +using namespace HTMLNames; + +static const AtomicString& slotNameFromAttributeValue(const AtomicString& value) +{ + return value == nullAtom ? SlotAssignment::defaultSlotName() : value; +} + +static const AtomicString& slotNameFromSlotAttribute(const Node& child) +{ + if (is<Text>(child)) + return SlotAssignment::defaultSlotName(); + + return slotNameFromAttributeValue(downcast<Element>(child).attributeWithoutSynchronization(slotAttr)); +} + +SlotAssignment::SlotAssignment() +{ +} + +SlotAssignment::~SlotAssignment() +{ +} + +HTMLSlotElement* SlotAssignment::findAssignedSlot(const Node& node, ShadowRoot& shadowRoot) +{ + if (!is<Text>(node) && !is<Element>(node)) + return nullptr; + + auto slotName = slotNameForHostChild(node); + auto it = m_slots.find(slotName); + if (it == m_slots.end()) + return nullptr; + + return findFirstSlotElement(*it->value, shadowRoot); +} + +void SlotAssignment::addSlotElementByName(const AtomicString& name, HTMLSlotElement& slotElement, ShadowRoot& shadowRoot) +{ +#ifndef NDEBUG + ASSERT(!m_slotElementsForConsistencyCheck.contains(&slotElement)); + m_slotElementsForConsistencyCheck.add(&slotElement); +#endif + + // FIXME: We should be able to do a targeted reconstruction. + shadowRoot.host()->invalidateStyleAndRenderersForSubtree(); + + const AtomicString& slotName = slotNameFromAttributeValue(name); + auto addResult = m_slots.add(slotName, std::unique_ptr<SlotInfo>()); + if (addResult.isNewEntry) { + addResult.iterator->value = std::make_unique<SlotInfo>(slotElement); + if (slotName == defaultSlotName()) // Because assignSlots doesn't collect nodes assigned to the default slot as an optimzation. + m_slotAssignmentsIsValid = false; + return; + } + + auto& slotInfo = *addResult.iterator->value; + + if (!slotInfo.hasSlotElements()) + slotInfo.element = &slotElement; + else { + slotInfo.element = nullptr; +#ifndef NDEBUG + m_needsToResolveSlotElements = true; +#endif + } + slotInfo.elementCount++; +} + +void SlotAssignment::removeSlotElementByName(const AtomicString& name, HTMLSlotElement& slotElement, ShadowRoot& shadowRoot) +{ +#ifndef NDEBUG + ASSERT(m_slotElementsForConsistencyCheck.contains(&slotElement)); + m_slotElementsForConsistencyCheck.remove(&slotElement); +#endif + + if (auto* host = shadowRoot.host()) // FIXME: We should be able to do a targeted reconstruction. + host->invalidateStyleAndRenderersForSubtree(); + + auto it = m_slots.find(slotNameFromAttributeValue(name)); + RELEASE_ASSERT(it != m_slots.end()); + + auto& slotInfo = *it->value; + RELEASE_ASSERT(slotInfo.hasSlotElements()); + + slotInfo.elementCount--; + if (slotInfo.element == &slotElement) { + slotInfo.element = nullptr; +#ifndef NDEBUG + m_needsToResolveSlotElements = true; +#endif + } + ASSERT(slotInfo.element || m_needsToResolveSlotElements); +} + +void SlotAssignment::didChangeSlot(const AtomicString& slotAttrValue, ShadowRoot& shadowRoot) +{ + auto& slotName = slotNameFromAttributeValue(slotAttrValue); + auto it = m_slots.find(slotName); + if (it == m_slots.end()) + return; + + it->value->assignedNodes.clear(); + m_slotAssignmentsIsValid = false; + + HTMLSlotElement* slotElement = findFirstSlotElement(*it->value, shadowRoot); + if (!slotElement) + return; + + shadowRoot.host()->invalidateStyleAndRenderersForSubtree(); + + if (shadowRoot.mode() == ShadowRootMode::UserAgent) + return; + + slotElement->enqueueSlotChangeEvent(); +} + +void SlotAssignment::hostChildElementDidChange(const Element& childElement, ShadowRoot& shadowRoot) +{ + didChangeSlot(childElement.attributeWithoutSynchronization(slotAttr), shadowRoot); +} + +const Vector<Node*>* SlotAssignment::assignedNodesForSlot(const HTMLSlotElement& slotElement, ShadowRoot& shadowRoot) +{ + ASSERT(slotElement.containingShadowRoot() == &shadowRoot); + const AtomicString& slotName = slotNameFromAttributeValue(slotElement.attributeWithoutSynchronization(nameAttr)); + auto it = m_slots.find(slotName); + RELEASE_ASSERT(it != m_slots.end()); + + auto& slotInfo = *it->value; + if (!m_slotAssignmentsIsValid) + assignSlots(shadowRoot); + + if (!slotInfo.assignedNodes.size()) + return nullptr; + + RELEASE_ASSERT(slotInfo.hasSlotElements()); + if (slotInfo.hasDuplicatedSlotElements() && findFirstSlotElement(slotInfo, shadowRoot) != &slotElement) + return nullptr; + + return &slotInfo.assignedNodes; +} + +const AtomicString& SlotAssignment::slotNameForHostChild(const Node& child) const +{ + return slotNameFromSlotAttribute(child); +} + +HTMLSlotElement* SlotAssignment::findFirstSlotElement(SlotInfo& slotInfo, ShadowRoot& shadowRoot) +{ + if (slotInfo.shouldResolveSlotElement()) + resolveAllSlotElements(shadowRoot); + +#ifndef NDEBUG + ASSERT(!slotInfo.element || m_slotElementsForConsistencyCheck.contains(slotInfo.element)); + ASSERT(!!slotInfo.element == !!slotInfo.elementCount); +#endif + + return slotInfo.element; +} + +void SlotAssignment::resolveAllSlotElements(ShadowRoot& shadowRoot) +{ +#ifndef NDEBUG + ASSERT(m_needsToResolveSlotElements); + m_needsToResolveSlotElements = false; +#endif + + // FIXME: It's inefficient to reset all values. We should be able to void this in common case. + for (auto& entry : m_slots) + entry.value->element = nullptr; + + unsigned slotCount = m_slots.size(); + for (auto& slotElement : descendantsOfType<HTMLSlotElement>(shadowRoot)) { + auto& slotName = slotNameFromAttributeValue(slotElement.attributeWithoutSynchronization(nameAttr)); + + auto it = m_slots.find(slotName); + RELEASE_ASSERT(it != m_slots.end()); + + SlotInfo& slotInfo = *it->value; + bool hasSeenSlotWithSameName = !!slotInfo.element; + if (hasSeenSlotWithSameName) + continue; + + slotInfo.element = &slotElement; + slotCount--; + if (!slotCount) + break; + } +} + +void SlotAssignment::assignSlots(ShadowRoot& shadowRoot) +{ + ASSERT(!m_slotAssignmentsIsValid); + m_slotAssignmentsIsValid = true; + + for (auto& entry : m_slots) + entry.value->assignedNodes.shrink(0); + + auto& host = *shadowRoot.host(); + for (auto* child = host.firstChild(); child; child = child->nextSibling()) { + if (!is<Text>(*child) && !is<Element>(*child)) + continue; + auto slotName = slotNameForHostChild(*child); + assignToSlot(*child, slotName); + } + + for (auto& entry : m_slots) + entry.value->assignedNodes.shrinkToFit(); +} + +void SlotAssignment::assignToSlot(Node& child, const AtomicString& slotName) +{ + ASSERT(!slotName.isNull()); + if (slotName == defaultSlotName()) { + auto defaultSlotEntry = m_slots.find(defaultSlotName()); + if (defaultSlotEntry != m_slots.end()) + defaultSlotEntry->value->assignedNodes.append(&child); + return; + } + + auto addResult = m_slots.add(slotName, std::make_unique<SlotInfo>()); + addResult.iterator->value->assignedNodes.append(&child); +} + +} + + diff --git a/Source/WebCore/dom/SlotAssignment.h b/Source/WebCore/dom/SlotAssignment.h new file mode 100644 index 000000000..e5ce9a77a --- /dev/null +++ b/Source/WebCore/dom/SlotAssignment.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "RenderTreeUpdater.h" +#include "ShadowRoot.h" +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/Vector.h> +#include <wtf/text/AtomicString.h> +#include <wtf/text/AtomicStringHash.h> + +namespace WebCore { + +class Element; +class HTMLSlotElement; +class Node; + +class SlotAssignment { + WTF_MAKE_NONCOPYABLE(SlotAssignment); WTF_MAKE_FAST_ALLOCATED; +public: + SlotAssignment(); + virtual ~SlotAssignment(); + + static const AtomicString& defaultSlotName() { return emptyAtom; } + + HTMLSlotElement* findAssignedSlot(const Node&, ShadowRoot&); + + void addSlotElementByName(const AtomicString&, HTMLSlotElement&, ShadowRoot&); + void removeSlotElementByName(const AtomicString&, HTMLSlotElement&, ShadowRoot&); + + void didChangeSlot(const AtomicString&, ShadowRoot&); + void enqueueSlotChangeEvent(const AtomicString&, ShadowRoot&); + + const Vector<Node*>* assignedNodesForSlot(const HTMLSlotElement&, ShadowRoot&); + + virtual void hostChildElementDidChange(const Element&, ShadowRoot&); + +private: + struct SlotInfo { + WTF_MAKE_FAST_ALLOCATED; + public: + SlotInfo() { } + SlotInfo(HTMLSlotElement& slotElement) + : element(&slotElement) + , elementCount(1) + { } + + bool hasSlotElements() { return !!elementCount; } + bool hasDuplicatedSlotElements() { return elementCount > 1; } + bool shouldResolveSlotElement() { return !element && elementCount; } + + HTMLSlotElement* element { nullptr }; + unsigned elementCount { 0 }; + Vector<Node*> assignedNodes; + }; + + virtual const AtomicString& slotNameForHostChild(const Node&) const; + + HTMLSlotElement* findFirstSlotElement(SlotInfo&, ShadowRoot&); + void resolveAllSlotElements(ShadowRoot&); + + void assignSlots(ShadowRoot&); + void assignToSlot(Node& child, const AtomicString& slotName); + + HashMap<AtomicString, std::unique_ptr<SlotInfo>> m_slots; + +#ifndef NDEBUG + HashSet<HTMLSlotElement*> m_slotElementsForConsistencyCheck; + bool m_needsToResolveSlotElements { false }; +#endif + + bool m_slotAssignmentsIsValid { false }; +}; + +inline void ShadowRoot::didRemoveAllChildrenOfShadowHost() +{ + if (m_slotAssignment) // FIXME: This is incorrect when there were no elements or text nodes removed. + m_slotAssignment->didChangeSlot(nullAtom, *this); +} + +inline void ShadowRoot::didChangeDefaultSlot() +{ + if (m_slotAssignment) + m_slotAssignment->didChangeSlot(nullAtom, *this); +} + +inline void ShadowRoot::hostChildElementDidChange(const Element& childElement) +{ + if (m_slotAssignment) + m_slotAssignment->hostChildElementDidChange(childElement, *this); +} + +inline void ShadowRoot::hostChildElementDidChangeSlotAttribute(Element& element, const AtomicString& oldValue, const AtomicString& newValue) +{ + if (!m_slotAssignment) + return; + m_slotAssignment->didChangeSlot(oldValue, *this); + m_slotAssignment->didChangeSlot(newValue, *this); + RenderTreeUpdater::tearDownRenderers(element); +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/Slotable.idl b/Source/WebCore/dom/Slotable.idl new file mode 100644 index 000000000..132eac7b2 --- /dev/null +++ b/Source/WebCore/dom/Slotable.idl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2016 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +[ + NoInterfaceObject, + EnabledAtRuntime=ShadowDOM, +] interface Slotable { + [ImplementedAs=assignedSlotForBindings] readonly attribute HTMLSlotElement? assignedSlot; +}; diff --git a/Source/WebCore/dom/SpaceSplitString.cpp b/Source/WebCore/dom/SpaceSplitString.cpp index b11c7d224..10c69abe6 100644 --- a/Source/WebCore/dom/SpaceSplitString.cpp +++ b/Source/WebCore/dom/SpaceSplitString.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2007 David Smith (catfish.man@gmail.com) - * Copyright (C) 2007, 2008, 2011, 2012 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2011-2014 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -22,42 +22,18 @@ #include "SpaceSplitString.h" #include "HTMLParserIdioms.h" -#include <wtf/ASCIICType.h> #include <wtf/HashMap.h> +#include <wtf/NeverDestroyed.h> #include <wtf/text/AtomicStringHash.h> -#include <wtf/text/StringBuilder.h> namespace WebCore { COMPILE_ASSERT(!(sizeof(SpaceSplitStringData) % sizeof(uintptr_t)), SpaceSplitStringDataTailIsAlignedToWordSize); -template <typename CharacterType> -static inline bool hasNonASCIIOrUpper(const CharacterType* characters, unsigned length) -{ - bool hasUpper = false; - CharacterType ored = 0; - for (unsigned i = 0; i < length; i++) { - CharacterType c = characters[i]; - hasUpper |= isASCIIUpper(c); - ored |= c; - } - return hasUpper || (ored & ~0x7F); -} - -static inline bool hasNonASCIIOrUpper(const String& string) -{ - unsigned length = string.length(); - - if (string.is8Bit()) - return hasNonASCIIOrUpper(string.characters8(), length); - return hasNonASCIIOrUpper(string.characters16(), length); -} - template <typename CharacterType, typename TokenProcessor> static inline void tokenizeSpaceSplitString(TokenProcessor& tokenProcessor, const CharacterType* characters, unsigned length) { - unsigned start = 0; - while (true) { + for (unsigned start = 0; ; ) { while (start < length && isHTMLSpace(characters[start])) ++start; if (start >= length) @@ -78,11 +54,11 @@ static inline void tokenizeSpaceSplitString(TokenProcessor& tokenProcessor, cons { ASSERT(!string.isNull()); - const StringImpl* stringImpl = string.impl(); - if (stringImpl->is8Bit()) - tokenizeSpaceSplitString(tokenProcessor, stringImpl->characters8(), stringImpl->length()); + const StringImpl& stringImpl = *string.impl(); + if (stringImpl.is8Bit()) + tokenizeSpaceSplitString(tokenProcessor, stringImpl.characters8(), stringImpl.length()); else - tokenizeSpaceSplitString(tokenProcessor, stringImpl->characters16(), stringImpl->length()); + tokenizeSpaceSplitString(tokenProcessor, stringImpl.characters16(), stringImpl.length()); } bool SpaceSplitStringData::containsAll(SpaceSplitStringData& other) @@ -100,7 +76,7 @@ bool SpaceSplitStringData::containsAll(SpaceSplitStringData& other) return true; } -struct SpaceSplitStringDataMapKeyTrait : public HashTraits<AtomicString> +struct SpaceSplitStringTableKeyTraits : public HashTraits<AtomicString> { // The number 200 for typicalNumberOfSpaceSplitString was based on the typical number of unique class names // on typical websites on August 2013. @@ -108,12 +84,12 @@ struct SpaceSplitStringDataMapKeyTrait : public HashTraits<AtomicString> static const int minimumTableSize = WTF::HashTableCapacityForSize<typicalNumberOfSpaceSplitString>::value; }; -typedef HashMap<AtomicString, SpaceSplitStringData*, DefaultHash<AtomicString>::Hash, SpaceSplitStringDataMapKeyTrait> SpaceSplitStringDataMap; +typedef HashMap<AtomicString, SpaceSplitStringData*, AtomicStringHash, SpaceSplitStringTableKeyTraits> SpaceSplitStringTable; -static SpaceSplitStringDataMap& sharedDataMap() +static SpaceSplitStringTable& spaceSplitStringTable() { - DEFINE_STATIC_LOCAL(SpaceSplitStringDataMap, map, ()); - return map; + static NeverDestroyed<SpaceSplitStringTable> table; + return table; } void SpaceSplitString::set(const AtomicString& inputString, bool shouldFoldCase) @@ -122,12 +98,7 @@ void SpaceSplitString::set(const AtomicString& inputString, bool shouldFoldCase) clear(); return; } - - AtomicString string(inputString); - if (shouldFoldCase && hasNonASCIIOrUpper(string)) - string = string.string().foldCase(); - - m_data = SpaceSplitStringData::create(string); + m_data = SpaceSplitStringData::create(shouldFoldCase ? inputString.convertToASCIILowercase() : inputString); } class TokenIsEqualToCStringTokenProcessor { @@ -162,12 +133,8 @@ bool SpaceSplitString::spaceSplitStringContainsValue(const String& inputString, if (inputString.isNull()) return false; - String string = inputString; - if (shouldFoldCase && hasNonASCIIOrUpper(string)) - string = string.foldCase(); - TokenIsEqualToCStringTokenProcessor tokenProcessor(value, valueLength); - tokenizeSpaceSplitString(tokenProcessor, string); + tokenizeSpaceSplitString(tokenProcessor, shouldFoldCase ? inputString.impl()->convertToASCIILowercase() : inputString); return tokenProcessor.referenceStringWasFound(); } @@ -176,8 +143,7 @@ class TokenCounter { public: TokenCounter() : m_tokenCount(0) { } - template <typename CharacterType> - bool processToken(const CharacterType*, unsigned) + template<typename CharacterType> bool processToken(const CharacterType*, unsigned) { ++m_tokenCount; return true; @@ -194,8 +160,7 @@ class TokenAtomicStringInitializer { public: TokenAtomicStringInitializer(AtomicString* memory) : m_memoryBucket(memory) { } - template <typename CharacterType> - bool processToken(const CharacterType* characters, unsigned length) + template<typename CharacterType> bool processToken(const CharacterType* characters, unsigned length) { new (NotNull, m_memoryBucket) AtomicString(characters, length); ++m_memoryBucket; @@ -203,11 +168,12 @@ public: } const AtomicString* nextMemoryBucket() const { return m_memoryBucket; } + private: AtomicString* m_memoryBucket; }; -PassRefPtr<SpaceSplitStringData> SpaceSplitStringData::create(const AtomicString& keyString, unsigned tokenCount) +inline Ref<SpaceSplitStringData> SpaceSplitStringData::create(const AtomicString& keyString, unsigned tokenCount) { ASSERT(tokenCount); @@ -222,18 +188,17 @@ PassRefPtr<SpaceSplitStringData> SpaceSplitStringData::create(const AtomicString ASSERT(static_cast<unsigned>(tokenInitializer.nextMemoryBucket() - tokenArrayStart) == tokenCount); ASSERT(reinterpret_cast<const char*>(tokenInitializer.nextMemoryBucket()) == reinterpret_cast<const char*>(spaceSplitStringData) + sizeToAllocate); - return adoptRef(spaceSplitStringData); + return adoptRef(*spaceSplitStringData); } -PassRefPtr<SpaceSplitStringData> SpaceSplitStringData::create(const AtomicString& keyString) +RefPtr<SpaceSplitStringData> SpaceSplitStringData::create(const AtomicString& keyString) { ASSERT(isMainThread()); ASSERT(!keyString.isNull()); - SpaceSplitStringDataMap& spaceSplitStringDataCache = sharedDataMap(); - SpaceSplitStringDataMap::iterator iterator = spaceSplitStringDataCache.find(keyString); - if (iterator != spaceSplitStringDataCache.end()) - return iterator->value; + auto addResult = spaceSplitStringTable().add(keyString, nullptr); + if (!addResult.isNewEntry) + return addResult.iterator->value; // Nothing in the cache? Let's create a new SpaceSplitStringData if the input has something useful. // 1) We find the number of strings in the input to know how much size we need to allocate. @@ -245,8 +210,8 @@ PassRefPtr<SpaceSplitStringData> SpaceSplitStringData::create(const AtomicString return nullptr; RefPtr<SpaceSplitStringData> spaceSplitStringData = create(keyString, tokenCount); - spaceSplitStringDataCache.add(keyString, spaceSplitStringData.get()); - return spaceSplitStringData.release(); + addResult.iterator->value = spaceSplitStringData.get(); + return spaceSplitStringData; } @@ -254,8 +219,7 @@ void SpaceSplitStringData::destroy(SpaceSplitStringData* spaceSplitString) { ASSERT(isMainThread()); - if (!spaceSplitString->m_keyString.isNull()) - sharedDataMap().remove(spaceSplitString->m_keyString); + spaceSplitStringTable().remove(spaceSplitString->m_keyString); unsigned i = 0; unsigned size = spaceSplitString->size(); diff --git a/Source/WebCore/dom/SpaceSplitString.h b/Source/WebCore/dom/SpaceSplitString.h index 2f0e0d35a..26fc99224 100644 --- a/Source/WebCore/dom/SpaceSplitString.h +++ b/Source/WebCore/dom/SpaceSplitString.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2007-2014 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -18,12 +18,9 @@ * */ -#ifndef SpaceSplitString_h -#define SpaceSplitString_h +#pragma once -#include <wtf/Assertions.h> #include <wtf/MainThread.h> -#include <wtf/Noncopyable.h> #include <wtf/text/AtomicString.h> namespace WebCore { @@ -32,7 +29,7 @@ class SpaceSplitStringData { WTF_MAKE_NONCOPYABLE(SpaceSplitStringData); WTF_MAKE_FAST_ALLOCATED; public: - static PassRefPtr<SpaceSplitStringData> create(const AtomicString&); + static RefPtr<SpaceSplitStringData> create(const AtomicString&); bool contains(const AtomicString& string) { @@ -51,26 +48,26 @@ public: unsigned size() const { return m_size; } static ptrdiff_t sizeMemoryOffset() { return OBJECT_OFFSETOF(SpaceSplitStringData, m_size); } - const AtomicString& operator[](size_t i) + const AtomicString& operator[](unsigned i) { RELEASE_ASSERT(i < m_size); return tokenArrayStart()[i]; } - inline void ref() + void ref() { ASSERT(isMainThread()); ASSERT(m_refCount); ++m_refCount; } - inline void deref() + void deref() { ASSERT(isMainThread()); ASSERT(m_refCount); unsigned tempRefCount = m_refCount - 1; if (!tempRefCount) { - SpaceSplitStringData::destroy(this); + destroy(this); return; } m_refCount = tempRefCount; @@ -79,7 +76,7 @@ public: static ptrdiff_t tokensMemoryOffset() { return sizeof(SpaceSplitStringData); } private: - static PassRefPtr<SpaceSplitStringData> create(const AtomicString&, unsigned tokenCount); + static Ref<SpaceSplitStringData> create(const AtomicString&, unsigned tokenCount); SpaceSplitStringData(const AtomicString& string, unsigned size) : m_keyString(string) , m_refCount(1) @@ -89,7 +86,7 @@ private: ASSERT_WITH_MESSAGE(m_size, "SpaceSplitStringData should never be empty by definition. There is no difference between empty and null."); } - inline ~SpaceSplitStringData() { } + ~SpaceSplitStringData() { } static void destroy(SpaceSplitStringData*); AtomicString* tokenArrayStart() { return reinterpret_cast<AtomicString*>(this + 1); } @@ -107,14 +104,14 @@ public: bool operator!=(const SpaceSplitString& other) const { return m_data != other.m_data; } void set(const AtomicString&, bool shouldFoldCase); - void clear() { m_data.clear(); } + void clear() { m_data = nullptr; } bool contains(const AtomicString& string) const { return m_data && m_data->contains(string); } bool containsAll(const SpaceSplitString& names) const { return !names.m_data || (m_data && m_data->containsAll(*names.m_data)); } - size_t size() const { return m_data ? m_data->size() : 0; } + unsigned size() const { return m_data ? m_data->size() : 0; } bool isEmpty() const { return !m_data; } - const AtomicString& operator[](size_t i) const + const AtomicString& operator[](unsigned i) const { ASSERT_WITH_SECURITY_IMPLICATION(m_data); return (*m_data)[i]; @@ -128,10 +125,7 @@ public: } private: - RefPtr<SpaceSplitStringData> m_data; }; } // namespace WebCore - -#endif // SpaceSplitString_h diff --git a/Source/WebCore/dom/StaticNodeList.cpp b/Source/WebCore/dom/StaticNodeList.cpp index e277c5015..4aff2ce5f 100644 --- a/Source/WebCore/dom/StaticNodeList.cpp +++ b/Source/WebCore/dom/StaticNodeList.cpp @@ -10,7 +10,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -39,17 +39,7 @@ unsigned StaticNodeList::length() const Node* StaticNodeList::item(unsigned index) const { if (index < m_nodes.size()) - return &const_cast<Node&>(m_nodes[index].get()); - return nullptr; -} - -Node* StaticNodeList::namedItem(const AtomicString& elementId) const -{ - for (unsigned i = 0, length = m_nodes.size(); i < length; ++i) { - Node& node = const_cast<Node&>(m_nodes[i].get()); - if (node.isElementNode() && toElement(node).getIdAttribute() == elementId) - return &node; - } + return const_cast<Node*>(m_nodes[index].ptr()); return nullptr; } @@ -58,20 +48,10 @@ unsigned StaticElementList::length() const return m_elements.size(); } -Node* StaticElementList::item(unsigned index) const +Element* StaticElementList::item(unsigned index) const { if (index < m_elements.size()) - return &const_cast<Element&>(m_elements[index].get()); - return nullptr; -} - -Node* StaticElementList::namedItem(const AtomicString& elementId) const -{ - for (unsigned i = 0, length = m_elements.size(); i < length; ++i) { - Element& element = const_cast<Element&>(m_elements[i].get()); - if (element.getIdAttribute() == elementId) - return &element; - } + return const_cast<Element*>(m_elements[index].ptr()); return nullptr; } diff --git a/Source/WebCore/dom/StaticNodeList.h b/Source/WebCore/dom/StaticNodeList.h index c67ae0f17..0f1cba9fb 100644 --- a/Source/WebCore/dom/StaticNodeList.h +++ b/Source/WebCore/dom/StaticNodeList.h @@ -10,7 +10,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -26,67 +26,49 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef StaticNodeList_h -#define StaticNodeList_h +#pragma once #include "Element.h" #include "NodeList.h" -#include <wtf/PassRefPtr.h> #include <wtf/RefPtr.h> #include <wtf/Vector.h> namespace WebCore { -class StaticNodeList final : public NodeList { +class WEBCORE_EXPORT StaticNodeList final : public NodeList { public: - static PassRefPtr<StaticNodeList> adopt(Vector<Ref<Node>>& nodes) + static Ref<StaticNodeList> create(Vector<Ref<Node>>&& nodes = { }) { - RefPtr<StaticNodeList> nodeList = adoptRef(new StaticNodeList); - nodeList->m_nodes.swap(nodes); - return nodeList.release(); + return adoptRef(*new StaticNodeList(WTFMove(nodes))); } - static PassRefPtr<StaticNodeList> createEmpty() - { - return adoptRef(new StaticNodeList); - } - - virtual unsigned length() const override; - virtual Node* item(unsigned index) const override; - virtual Node* namedItem(const AtomicString&) const override; + unsigned length() const override; + Node* item(unsigned index) const override; private: - StaticNodeList() { } + StaticNodeList(Vector<Ref<Node>>&& nodes) + : m_nodes(WTFMove(nodes)) + { } Vector<Ref<Node>> m_nodes; }; class StaticElementList final : public NodeList { public: - static PassRefPtr<StaticElementList> adopt(Vector<Ref<Element>>& elements) + static Ref<StaticElementList> create(Vector<Ref<Element>>&& elements = { }) { - RefPtr<StaticElementList> nodeList = adoptRef(new StaticElementList); - nodeList->m_elements.swap(elements); - return nodeList.release(); + return adoptRef(*new StaticElementList(WTFMove(elements))); } - static PassRefPtr<StaticElementList> createEmpty() - { - return adoptRef(new StaticElementList); - } - - virtual unsigned length() const override; - virtual Node* item(unsigned index) const override; - virtual Node* namedItem(const AtomicString&) const override; + unsigned length() const override; + Element* item(unsigned index) const override; private: - StaticElementList() - { - } + StaticElementList(Vector<Ref<Element>>&& elements) + : m_elements(WTFMove(elements)) + { } Vector<Ref<Element>> m_elements; }; } // namespace WebCore - -#endif // StaticNodeList_h diff --git a/Source/WebCore/dom/StaticRange.cpp b/Source/WebCore/dom/StaticRange.cpp new file mode 100644 index 000000000..6f8eb94dd --- /dev/null +++ b/Source/WebCore/dom/StaticRange.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "StaticRange.h" + +#include "Node.h" +#include "Range.h" + +namespace WebCore { + +StaticRange::StaticRange(Ref<Node>&& startContainer, unsigned startOffset, Ref<Node>&& endContainer, unsigned endOffset) + : m_startContainer(WTFMove(startContainer)) + , m_startOffset(startOffset) + , m_endContainer(WTFMove(endContainer)) + , m_endOffset(endOffset) +{ +} + +StaticRange::~StaticRange() +{ +} + +Ref<StaticRange> StaticRange::create(Ref<Node>&& startContainer, unsigned startOffset, Ref<Node>&& endContainer, unsigned endOffset) +{ + return adoptRef(*new StaticRange(WTFMove(startContainer), startOffset, WTFMove(endContainer), endOffset)); +} + +Ref<StaticRange> StaticRange::createFromRange(const Range& range) +{ + return StaticRange::create(range.startContainer(), range.startOffset(), range.endContainer(), range.endOffset()); +} + +Node* StaticRange::startContainer() const +{ + return (Node*)m_startContainer.ptr(); +} + +Node* StaticRange::endContainer() const +{ + return (Node*)m_endContainer.ptr(); +} + +bool StaticRange::collapsed() const +{ + return m_startOffset == m_endOffset && m_startContainer.ptr() == m_endContainer.ptr(); +} + +} diff --git a/Source/WebCore/dom/StaticRange.h b/Source/WebCore/dom/StaticRange.h new file mode 100644 index 000000000..9a74eb312 --- /dev/null +++ b/Source/WebCore/dom/StaticRange.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class Node; +class Range; + +class StaticRange : public RefCounted<StaticRange> { +public: + ~StaticRange(); + + static Ref<StaticRange> createFromRange(const Range&); + static Ref<StaticRange> create(Ref<Node>&& startContainer, unsigned startOffset, Ref<Node>&& endContainer, unsigned endOffset); + + unsigned startOffset() const { return m_startOffset; } + unsigned endOffset() const { return m_endOffset; } + Node* startContainer() const; + Node* endContainer() const; + bool collapsed() const; + +private: + StaticRange(Ref<Node>&& startContainer, unsigned startOffset, Ref<Node>&& endContainer, unsigned endOffset); + + Ref<Node> m_startContainer; + unsigned m_startOffset; + Ref<Node> m_endContainer; + unsigned m_endOffset; +}; + +} diff --git a/Source/WebCore/dom/StaticRange.idl b/Source/WebCore/dom/StaticRange.idl new file mode 100644 index 000000000..4059584f5 --- /dev/null +++ b/Source/WebCore/dom/StaticRange.idl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 Apple, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + EnabledAtRuntime=InputEvents, + ImplementationLacksVTable, +] interface StaticRange { + readonly attribute unsigned long startOffset; + readonly attribute unsigned long endOffset; + readonly attribute Node startContainer; + readonly attribute Node endContainer; + readonly attribute boolean collapsed; +}; diff --git a/Source/WebCore/dom/StringCallback.cpp b/Source/WebCore/dom/StringCallback.cpp index b54105fea..8253b4b46 100644 --- a/Source/WebCore/dom/StringCallback.cpp +++ b/Source/WebCore/dom/StringCallback.cpp @@ -36,36 +36,12 @@ namespace WebCore { -namespace { - -class DispatchCallbackTask : public ScriptExecutionContext::Task { -public: - static PassOwnPtr<DispatchCallbackTask> create(PassRefPtr<StringCallback> callback, const String& data) - { - return adoptPtr(new DispatchCallbackTask(callback, data)); - } - - virtual void performTask(ScriptExecutionContext*) override - { - m_callback->handleEvent(m_data); - } - -private: - DispatchCallbackTask(PassRefPtr<StringCallback> callback, const String& data) - : m_callback(callback) - , m_data(data) - { - } - - RefPtr<StringCallback> m_callback; - const String m_data; -}; - -} // namespace - void StringCallback::scheduleCallback(ScriptExecutionContext* context, const String& data) { - context->postTask(DispatchCallbackTask::create(this, data)); + RefPtr<StringCallback> protectedThis(this); + context->postTask([protectedThis, data] (ScriptExecutionContext&) { + protectedThis->handleEvent(data); + }); } } // namespace WebCore diff --git a/Source/WebCore/dom/StringCallback.h b/Source/WebCore/dom/StringCallback.h index 92e83e219..f3aaaa53a 100644 --- a/Source/WebCore/dom/StringCallback.h +++ b/Source/WebCore/dom/StringCallback.h @@ -28,8 +28,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef StringCallback_h -#define StringCallback_h +#pragma once #include <wtf/Forward.h> #include <wtf/RefCounted.h> @@ -48,5 +47,3 @@ public: }; } // namespace WebCore - -#endif // StringCallback_h diff --git a/Source/WebCore/dom/StringCallback.idl b/Source/WebCore/dom/StringCallback.idl index 9ab79c13c..5ccd31a47 100644 --- a/Source/WebCore/dom/StringCallback.idl +++ b/Source/WebCore/dom/StringCallback.idl @@ -28,6 +28,4 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -callback interface StringCallback { - boolean handleEvent(DOMString data); -}; +callback StringCallback = void (DOMString data); diff --git a/Source/WebCore/dom/StyledElement.cpp b/Source/WebCore/dom/StyledElement.cpp index a942efb59..2ee9997fd 100644 --- a/Source/WebCore/dom/StyledElement.cpp +++ b/Source/WebCore/dom/StyledElement.cpp @@ -3,7 +3,7 @@ * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Peter Kelly (pmk@post.com) * (C) 2001 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2004, 2005, 2006, 2008, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2008, 2010, 2014 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -28,8 +28,10 @@ #include "CSSParser.h" #include "CSSStyleSheet.h" #include "CSSValuePool.h" +#include "CachedResource.h" #include "ContentSecurityPolicy.h" #include "DOMTokenList.h" +#include "HTMLElement.h" #include "HTMLParserIdioms.h" #include "InspectorInstrumentation.h" #include "PropertySetCSSStyleDeclaration.h" @@ -44,87 +46,13 @@ COMPILE_ASSERT(sizeof(StyledElement) == sizeof(Element), styledelement_should_re using namespace HTMLNames; -struct PresentationAttributeCacheKey { - PresentationAttributeCacheKey() : tagName(0) { } - AtomicStringImpl* tagName; - // Only the values need refcounting. - Vector<std::pair<AtomicStringImpl*, AtomicString>, 3> attributesAndValues; -}; - -struct PresentationAttributeCacheEntry { - WTF_MAKE_FAST_ALLOCATED; -public: - PresentationAttributeCacheKey key; - RefPtr<StyleProperties> value; -}; - -typedef HashMap<unsigned, OwnPtr<PresentationAttributeCacheEntry>, AlreadyHashed> PresentationAttributeCache; - -static bool operator!=(const PresentationAttributeCacheKey& a, const PresentationAttributeCacheKey& b) -{ - if (a.tagName != b.tagName) - return true; - return a.attributesAndValues != b.attributesAndValues; -} - -static PresentationAttributeCache& presentationAttributeCache() -{ - DEFINE_STATIC_LOCAL(PresentationAttributeCache, cache, ()); - return cache; -} - -class PresentationAttributeCacheCleaner { - WTF_MAKE_NONCOPYABLE(PresentationAttributeCacheCleaner); WTF_MAKE_FAST_ALLOCATED; -public: - PresentationAttributeCacheCleaner() - : m_hitCount(0) - , m_cleanTimer(this, &PresentationAttributeCacheCleaner::cleanCache) - { - } - - void didHitPresentationAttributeCache() - { - if (presentationAttributeCache().size() < minimumPresentationAttributeCacheSizeForCleaning) - return; - - m_hitCount++; - - if (!m_cleanTimer.isActive()) - m_cleanTimer.startOneShot(presentationAttributeCacheCleanTimeInSeconds); - } - -private: - static const unsigned presentationAttributeCacheCleanTimeInSeconds = 60; - static const int minimumPresentationAttributeCacheSizeForCleaning = 100; - static const unsigned minimumPresentationAttributeCacheHitCountPerMinute = (100 * presentationAttributeCacheCleanTimeInSeconds) / 60; - - void cleanCache(Timer<PresentationAttributeCacheCleaner>* timer) - { - ASSERT_UNUSED(timer, timer == &m_cleanTimer); - unsigned hitCount = m_hitCount; - m_hitCount = 0; - if (hitCount > minimumPresentationAttributeCacheHitCountPerMinute) - return; - presentationAttributeCache().clear(); - } - - unsigned m_hitCount; - Timer<PresentationAttributeCacheCleaner> m_cleanTimer; -}; - -static PresentationAttributeCacheCleaner& presentationAttributeCacheCleaner() +void StyledElement::synchronizeStyleAttributeInternal(StyledElement* styledElement) { - DEFINE_STATIC_LOCAL(PresentationAttributeCacheCleaner, cleaner, ()); - return cleaner; -} - -void StyledElement::synchronizeStyleAttributeInternal() const -{ - ASSERT(elementData()); - ASSERT(elementData()->styleAttributeIsDirty()); - elementData()->setStyleAttributeIsDirty(false); - if (const StyleProperties* inlineStyle = this->inlineStyle()) - const_cast<StyledElement*>(this)->setSynchronizedLazyAttribute(styleAttr, inlineStyle->asText()); + ASSERT(styledElement->elementData()); + ASSERT(styledElement->elementData()->styleAttributeIsDirty()); + styledElement->elementData()->setStyleAttributeIsDirty(false); + if (const StyleProperties* inlineStyle = styledElement->inlineStyle()) + styledElement->setSynchronizedLazyAttribute(styleAttr, inlineStyle->asText()); } StyledElement::~StyledElement() @@ -133,7 +61,7 @@ StyledElement::~StyledElement() cssomWrapper->clearParentElement(); } -CSSStyleDeclaration* StyledElement::style() +CSSStyleDeclaration* StyledElement::cssomStyle() { return ensureMutableInlineStyle().ensureInlineCSSStyleDeclaration(this); } @@ -143,19 +71,20 @@ MutableStyleProperties& StyledElement::ensureMutableInlineStyle() RefPtr<StyleProperties>& inlineStyle = ensureUniqueElementData().m_inlineStyle; if (!inlineStyle) inlineStyle = MutableStyleProperties::create(strictToCSSParserMode(isHTMLElement() && !document().inQuirksMode())); - else if (!inlineStyle->isMutable()) + else if (!is<MutableStyleProperties>(*inlineStyle)) inlineStyle = inlineStyle->mutableCopy(); - ASSERT_WITH_SECURITY_IMPLICATION(inlineStyle->isMutable()); - return static_cast<MutableStyleProperties&>(*inlineStyle); + return downcast<MutableStyleProperties>(*inlineStyle); } void StyledElement::attributeChanged(const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue, AttributeModificationReason reason) { - if (name == styleAttr) - styleAttributeChanged(newValue, reason); - else if (isPresentationAttribute(name)) { - elementData()->setPresentationAttributeStyleIsDirty(true); - setNeedsStyleRecalc(InlineStyleChange); + if (oldValue != newValue) { + if (name == styleAttr) + styleAttributeChanged(newValue, reason); + else if (isPresentationAttribute(name)) { + elementData()->setPresentationAttributeStyleIsDirty(true); + invalidateStyle(); + } } Element::attributeChanged(name, oldValue, newValue, reason); @@ -170,7 +99,12 @@ PropertySetCSSStyleDeclaration* StyledElement::inlineStyleCSSOMWrapper() return cssomWrapper; } -inline void StyledElement::setInlineStyleFromString(const AtomicString& newStyleString) +static bool usesStyleBasedEditability(const StyleProperties& properties) +{ + return properties.getPropertyCSSValue(CSSPropertyWebkitUserModify); +} + +void StyledElement::setInlineStyleFromString(const AtomicString& newStyleString) { RefPtr<StyleProperties>& inlineStyle = elementData()->m_inlineStyle; @@ -180,15 +114,16 @@ inline void StyledElement::setInlineStyleFromString(const AtomicString& newStyle // We reconstruct the property set instead of mutating if there is no CSSOM wrapper. // This makes wrapperless property sets immutable and so cacheable. - if (inlineStyle && !inlineStyle->isMutable()) - inlineStyle.clear(); + if (inlineStyle && !is<MutableStyleProperties>(*inlineStyle)) + inlineStyle = nullptr; if (!inlineStyle) inlineStyle = CSSParser::parseInlineStyleDeclaration(newStyleString, this); - else { - ASSERT(inlineStyle->isMutable()); - static_pointer_cast<MutableStyleProperties>(inlineStyle)->parseDeclaration(newStyleString, &document().elementSheet().contents()); - } + else + downcast<MutableStyleProperties>(*inlineStyle).parseDeclaration(newStyleString, document()); + + if (usesStyleBasedEditability(*inlineStyle)) + document().setHasElementUsingStyleBasedEditability(); } void StyledElement::styleAttributeChanged(const AtomicString& newStyleString, AttributeModificationReason reason) @@ -200,48 +135,55 @@ void StyledElement::styleAttributeChanged(const AtomicString& newStyleString, At if (newStyleString.isNull()) { if (PropertySetCSSStyleDeclaration* cssomWrapper = inlineStyleCSSOMWrapper()) cssomWrapper->clearParentElement(); - ensureUniqueElementData().m_inlineStyle.clear(); - } else if (reason == ModifiedByCloning || document().contentSecurityPolicy()->allowInlineStyle(document().url(), startLineNumber)) + ensureUniqueElementData().m_inlineStyle = nullptr; + } else if (reason == ModifiedByCloning || document().contentSecurityPolicy()->allowInlineStyle(document().url(), startLineNumber, String(), isInUserAgentShadowTree())) setInlineStyleFromString(newStyleString); elementData()->setStyleAttributeIsDirty(false); - setNeedsStyleRecalc(InlineStyleChange); - InspectorInstrumentation::didInvalidateStyleAttr(&document(), this); + invalidateStyle(); + InspectorInstrumentation::didInvalidateStyleAttr(document(), *this); } -void StyledElement::inlineStyleChanged() +void StyledElement::invalidateStyleAttribute() { - setNeedsStyleRecalc(InlineStyleChange); - ASSERT(elementData()); + if (usesStyleBasedEditability(*inlineStyle())) + document().setHasElementUsingStyleBasedEditability(); + elementData()->setStyleAttributeIsDirty(true); - InspectorInstrumentation::didInvalidateStyleAttr(&document(), this); + invalidateStyle(); +} + +void StyledElement::inlineStyleChanged() +{ + invalidateStyleAttribute(); + InspectorInstrumentation::didInvalidateStyleAttr(document(), *this); } bool StyledElement::setInlineStyleProperty(CSSPropertyID propertyID, CSSValueID identifier, bool important) { - ensureMutableInlineStyle().setProperty(propertyID, cssValuePool().createIdentifierValue(identifier), important); + ensureMutableInlineStyle().setProperty(propertyID, CSSValuePool::singleton().createIdentifierValue(identifier), important); inlineStyleChanged(); return true; } bool StyledElement::setInlineStyleProperty(CSSPropertyID propertyID, CSSPropertyID identifier, bool important) { - ensureMutableInlineStyle().setProperty(propertyID, cssValuePool().createIdentifierValue(identifier), important); + ensureMutableInlineStyle().setProperty(propertyID, CSSValuePool::singleton().createIdentifierValue(identifier), important); inlineStyleChanged(); return true; } -bool StyledElement::setInlineStyleProperty(CSSPropertyID propertyID, double value, CSSPrimitiveValue::UnitTypes unit, bool important) +bool StyledElement::setInlineStyleProperty(CSSPropertyID propertyID, double value, CSSPrimitiveValue::UnitType unit, bool important) { - ensureMutableInlineStyle().setProperty(propertyID, cssValuePool().createValue(value, unit), important); + ensureMutableInlineStyle().setProperty(propertyID, CSSValuePool::singleton().createValue(value, unit), important); inlineStyleChanged(); return true; } bool StyledElement::setInlineStyleProperty(CSSPropertyID propertyID, const String& value, bool important) { - bool changes = ensureMutableInlineStyle().setProperty(propertyID, value, important, &document().elementSheet().contents()); + bool changes = ensureMutableInlineStyle().setProperty(propertyID, value, important, CSSParserContext(document())); if (changes) inlineStyleChanged(); return changes; @@ -267,111 +209,41 @@ void StyledElement::removeAllInlineStyleProperties() void StyledElement::addSubresourceAttributeURLs(ListHashSet<URL>& urls) const { - if (const StyleProperties* inlineStyle = elementData() ? elementData()->inlineStyle() : 0) - inlineStyle->addSubresourceStyleURLs(urls, &document().elementSheet().contents()); -} - -static inline bool attributeNameSort(const std::pair<AtomicStringImpl*, AtomicString>& p1, const std::pair<AtomicStringImpl*, AtomicString>& p2) -{ - // Sort based on the attribute name pointers. It doesn't matter what the order is as long as it is always the same. - return p1.first < p2.first; -} - -void StyledElement::makePresentationAttributeCacheKey(PresentationAttributeCacheKey& result) const -{ - // FIXME: Enable for SVG. - if (namespaceURI() != xhtmlNamespaceURI) - return; - // Interpretation of the size attributes on <input> depends on the type attribute. - if (hasTagName(inputTag)) - return; - for (const Attribute& attribute : attributesIterator()) { - if (!isPresentationAttribute(attribute.name())) - continue; - if (!attribute.namespaceURI().isNull()) - return; - // FIXME: Background URL may depend on the base URL and can't be shared. Disallow caching. - if (attribute.name() == backgroundAttr) - return; - result.attributesAndValues.append(std::make_pair(attribute.localName().impl(), attribute.value())); - } - if (result.attributesAndValues.isEmpty()) + auto* inlineStyle = this->inlineStyle(); + if (!inlineStyle) return; - // Attribute order doesn't matter. Sort for easy equality comparison. - std::sort(result.attributesAndValues.begin(), result.attributesAndValues.end(), attributeNameSort); - // The cache key is non-null when the tagName is set. - result.tagName = localName().impl(); -} - -static unsigned computePresentationAttributeCacheHash(const PresentationAttributeCacheKey& key) -{ - if (!key.tagName) - return 0; - ASSERT(key.attributesAndValues.size()); - unsigned attributeHash = StringHasher::hashMemory(key.attributesAndValues.data(), key.attributesAndValues.size() * sizeof(key.attributesAndValues[0])); - return WTF::pairIntHash(key.tagName->existingHash(), attributeHash); + inlineStyle->traverseSubresources([&] (auto& resource) { + urls.add(resource.url()); + return false; + }); } void StyledElement::rebuildPresentationAttributeStyle() { - PresentationAttributeCacheKey cacheKey; - makePresentationAttributeCacheKey(cacheKey); - - unsigned cacheHash = computePresentationAttributeCacheHash(cacheKey); - - PresentationAttributeCache::iterator cacheIterator; - if (cacheHash) { - cacheIterator = presentationAttributeCache().add(cacheHash, nullptr).iterator; - if (cacheIterator->value && cacheIterator->value->key != cacheKey) - cacheHash = 0; - } else - cacheIterator = presentationAttributeCache().end(); - - RefPtr<StyleProperties> style; - if (cacheHash && cacheIterator->value) { - style = cacheIterator->value->value; - presentationAttributeCacheCleaner().didHitPresentationAttributeCache(); - } else { - style = MutableStyleProperties::create(isSVGElement() ? SVGAttributeMode : CSSQuirksMode); - for (const Attribute& attribute : attributesIterator()) - collectStyleForPresentationAttribute(attribute.name(), attribute.value(), static_cast<MutableStyleProperties&>(*style)); - } + RefPtr<StyleProperties> style = MutableStyleProperties::create(isSVGElement() ? SVGAttributeMode : HTMLQuirksMode); + for (const Attribute& attribute : attributesIterator()) + collectStyleForPresentationAttribute(attribute.name(), attribute.value(), static_cast<MutableStyleProperties&>(*style)); // ShareableElementData doesn't store presentation attribute style, so make sure we have a UniqueElementData. UniqueElementData& elementData = ensureUniqueElementData(); elementData.setPresentationAttributeStyleIsDirty(false); - elementData.m_presentationAttributeStyle = style->isEmpty() ? 0 : style; - - if (!cacheHash || cacheIterator->value) - return; - - OwnPtr<PresentationAttributeCacheEntry> newEntry = adoptPtr(new PresentationAttributeCacheEntry); - newEntry->key = cacheKey; - newEntry->value = style.release(); - - static const int presentationAttributeCacheMaximumSize = 4096; - if (presentationAttributeCache().size() > presentationAttributeCacheMaximumSize) { - // Start building from scratch if the cache ever gets big. - presentationAttributeCache().clear(); - presentationAttributeCache().set(cacheHash, newEntry.release()); - } else - cacheIterator->value = newEntry.release(); + elementData.m_presentationAttributeStyle = style->isEmpty() ? nullptr : WTFMove(style); } void StyledElement::addPropertyToPresentationAttributeStyle(MutableStyleProperties& style, CSSPropertyID propertyID, CSSValueID identifier) { - style.setProperty(propertyID, cssValuePool().createIdentifierValue(identifier)); + style.setProperty(propertyID, CSSValuePool::singleton().createIdentifierValue(identifier)); } -void StyledElement::addPropertyToPresentationAttributeStyle(MutableStyleProperties& style, CSSPropertyID propertyID, double value, CSSPrimitiveValue::UnitTypes unit) +void StyledElement::addPropertyToPresentationAttributeStyle(MutableStyleProperties& style, CSSPropertyID propertyID, double value, CSSPrimitiveValue::UnitType unit) { - style.setProperty(propertyID, cssValuePool().createValue(value, unit)); + style.setProperty(propertyID, CSSValuePool::singleton().createValue(value, unit)); } void StyledElement::addPropertyToPresentationAttributeStyle(MutableStyleProperties& style, CSSPropertyID propertyID, const String& value) { - style.setProperty(propertyID, value, false, &document().elementSheet().contents()); + style.setProperty(propertyID, value, false, CSSParserContext(document())); } } diff --git a/Source/WebCore/dom/StyledElement.h b/Source/WebCore/dom/StyledElement.h index 040a093cc..442041cd5 100644 --- a/Source/WebCore/dom/StyledElement.h +++ b/Source/WebCore/dom/StyledElement.h @@ -3,7 +3,7 @@ * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Peter Kelly (pmk@post.com) * (C) 2001 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2014 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -22,8 +22,7 @@ * */ -#ifndef StyledElement_h -#define StyledElement_h +#pragma once #include "CSSPrimitiveValue.h" #include "CSSPropertyNames.h" @@ -37,29 +36,28 @@ class MutableStyleProperties; class PropertySetCSSStyleDeclaration; class StyleProperties; -struct PresentationAttributeCacheKey; - class StyledElement : public Element { public: virtual ~StyledElement(); - virtual const StyleProperties* additionalPresentationAttributeStyle() { return 0; } + virtual const StyleProperties* additionalPresentationAttributeStyle() const { return nullptr; } void invalidateStyleAttribute(); - const StyleProperties* inlineStyle() const { return elementData() ? elementData()->m_inlineStyle.get() : 0; } + const StyleProperties* inlineStyle() const { return elementData() ? elementData()->m_inlineStyle.get() : nullptr; } bool setInlineStyleProperty(CSSPropertyID, CSSValueID identifier, bool important = false); bool setInlineStyleProperty(CSSPropertyID, CSSPropertyID identifier, bool important = false); - bool setInlineStyleProperty(CSSPropertyID, double value, CSSPrimitiveValue::UnitTypes, bool important = false); - bool setInlineStyleProperty(CSSPropertyID, const String& value, bool important = false); + WEBCORE_EXPORT bool setInlineStyleProperty(CSSPropertyID, double value, CSSPrimitiveValue::UnitType, bool important = false); + WEBCORE_EXPORT bool setInlineStyleProperty(CSSPropertyID, const String& value, bool important = false); bool removeInlineStyleProperty(CSSPropertyID); void removeAllInlineStyleProperties(); - void synchronizeStyleAttributeInternal() const; + static void synchronizeStyleAttributeInternal(StyledElement*); + void synchronizeStyleAttributeInternal() const { StyledElement::synchronizeStyleAttributeInternal(const_cast<StyledElement*>(this)); } - virtual CSSStyleDeclaration* style() override final; + CSSStyleDeclaration* cssomStyle() final; - const StyleProperties* presentationAttributeStyle(); + const StyleProperties* presentationAttributeStyle() const; virtual void collectStyleForPresentationAttribute(const QualifiedName&, const AtomicString&, MutableStyleProperties&) { } protected: @@ -68,15 +66,15 @@ protected: { } - virtual void attributeChanged(const QualifiedName&, const AtomicString& oldValue, const AtomicString& newValue, AttributeModificationReason = ModifiedDirectly) override; + void attributeChanged(const QualifiedName&, const AtomicString& oldValue, const AtomicString& newValue, AttributeModificationReason = ModifiedDirectly) override; virtual bool isPresentationAttribute(const QualifiedName&) const { return false; } void addPropertyToPresentationAttributeStyle(MutableStyleProperties&, CSSPropertyID, CSSValueID identifier); - void addPropertyToPresentationAttributeStyle(MutableStyleProperties&, CSSPropertyID, double value, CSSPrimitiveValue::UnitTypes); + void addPropertyToPresentationAttributeStyle(MutableStyleProperties&, CSSPropertyID, double value, CSSPrimitiveValue::UnitType); void addPropertyToPresentationAttributeStyle(MutableStyleProperties&, CSSPropertyID, const String& value); - virtual void addSubresourceAttributeURLs(ListHashSet<URL>&) const override; + void addSubresourceAttributeURLs(ListHashSet<URL>&) const override; private: void styleAttributeChanged(const AtomicString& newStyleString, AttributeModificationReason); @@ -86,29 +84,20 @@ private: void setInlineStyleFromString(const AtomicString&); MutableStyleProperties& ensureMutableInlineStyle(); - void makePresentationAttributeCacheKey(PresentationAttributeCacheKey&) const; void rebuildPresentationAttributeStyle(); }; -inline void StyledElement::invalidateStyleAttribute() -{ - ASSERT(elementData()); - elementData()->setStyleAttributeIsDirty(true); -} - -inline const StyleProperties* StyledElement::presentationAttributeStyle() +inline const StyleProperties* StyledElement::presentationAttributeStyle() const { if (!elementData()) - return 0; + return nullptr; if (elementData()->presentationAttributeStyleIsDirty()) - rebuildPresentationAttributeStyle(); + const_cast<StyledElement&>(*this).rebuildPresentationAttributeStyle(); return elementData()->presentationAttributeStyle(); } -inline bool isStyledElement(const Node& node) { return node.isStyledElement(); } - -NODE_TYPE_CASTS(StyledElement) - -} //namespace +} // namespace WebCore -#endif +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::StyledElement) + static bool isType(const WebCore::Node& node) { return node.isStyledElement(); } +SPECIALIZE_TYPE_TRAITS_END() diff --git a/Source/WebCore/dom/SuccessOr.h b/Source/WebCore/dom/SuccessOr.h new file mode 100644 index 000000000..65a30591b --- /dev/null +++ b/Source/WebCore/dom/SuccessOr.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <wtf/Optional.h> + +namespace WebCore { + +template <typename T> +class SuccessOr : public std::optional<T> { +public: + SuccessOr() : std::optional<T>() { } + SuccessOr(T&& error) : std::optional<T>(error) { } + + explicit constexpr operator bool() const { return !std::optional<T>::operator bool(); } +}; + +} // namespace WebCore diff --git a/Source/WebCore/dom/TagCollection.cpp b/Source/WebCore/dom/TagCollection.cpp new file mode 100644 index 000000000..7f9f40326 --- /dev/null +++ b/Source/WebCore/dom/TagCollection.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004-2007, 2014-2016 Apple Inc. All rights reserved. + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "TagCollection.h" + +#include "NodeRareData.h" + +namespace WebCore { + +TagCollectionNS::TagCollectionNS(ContainerNode& rootNode, const AtomicString& namespaceURI, const AtomicString& localName) + : CachedHTMLCollection<TagCollectionNS, CollectionTypeTraits<ByTag>::traversalType>(rootNode, ByTag) + , m_namespaceURI(namespaceURI) + , m_localName(localName) +{ + ASSERT(m_namespaceURI.isNull() || !m_namespaceURI.isEmpty()); +} + +TagCollectionNS::~TagCollectionNS() +{ + ownerNode().nodeLists()->removeCachedTagCollectionNS(*this, m_namespaceURI, m_localName); +} + +TagCollection::TagCollection(ContainerNode& rootNode, const AtomicString& qualifiedName) + : CachedHTMLCollection<TagCollection, CollectionTypeTraits<ByTag>::traversalType>(rootNode, ByTag) + , m_qualifiedName(qualifiedName) +{ + ASSERT(qualifiedName != starAtom); +} + +TagCollection::~TagCollection() +{ + ownerNode().nodeLists()->removeCachedCollection(this, m_qualifiedName); +} + +HTMLTagCollection::HTMLTagCollection(ContainerNode& rootNode, const AtomicString& qualifiedName) + : CachedHTMLCollection<HTMLTagCollection, CollectionTypeTraits<ByHTMLTag>::traversalType>(rootNode, ByHTMLTag) + , m_qualifiedName(qualifiedName) + , m_loweredQualifiedName(qualifiedName.convertToASCIILowercase()) +{ + ASSERT(qualifiedName != starAtom); +} + +HTMLTagCollection::~HTMLTagCollection() +{ + ownerNode().nodeLists()->removeCachedCollection(this, m_qualifiedName); +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/TagCollection.h b/Source/WebCore/dom/TagCollection.h new file mode 100644 index 000000000..2c02747c6 --- /dev/null +++ b/Source/WebCore/dom/TagCollection.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004-2008, 2014-2016 Apple Inc. All rights reserved. + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include "CachedHTMLCollection.h" +#include <wtf/text/AtomicString.h> + +namespace WebCore { + +// HTMLCollection that limits to a particular tag. +class TagCollection final : public CachedHTMLCollection<TagCollection, CollectionTypeTraits<ByTag>::traversalType> { +public: + static Ref<TagCollection> create(ContainerNode& rootNode, CollectionType type, const AtomicString& qualifiedName) + { + ASSERT_UNUSED(type, type == ByTag); + return adoptRef(*new TagCollection(rootNode, qualifiedName)); + } + + virtual ~TagCollection(); + bool elementMatches(Element&) const; + +private: + TagCollection(ContainerNode& rootNode, const AtomicString& qualifiedName); + + AtomicString m_qualifiedName; +}; + +class TagCollectionNS final : public CachedHTMLCollection<TagCollectionNS, CollectionTypeTraits<ByTag>::traversalType> { +public: + static Ref<TagCollectionNS> create(ContainerNode& rootNode, const AtomicString& namespaceURI, const AtomicString& localName) + { + return adoptRef(*new TagCollectionNS(rootNode, namespaceURI, localName)); + } + + virtual ~TagCollectionNS(); + bool elementMatches(Element&) const; + +private: + TagCollectionNS(ContainerNode& rootNode, const AtomicString& namespaceURI, const AtomicString& localName); + + AtomicString m_namespaceURI; + AtomicString m_localName; +}; + +class HTMLTagCollection final : public CachedHTMLCollection<HTMLTagCollection, CollectionTypeTraits<ByHTMLTag>::traversalType> { +public: + static Ref<HTMLTagCollection> create(ContainerNode& rootNode, CollectionType type, const AtomicString& qualifiedName) + { + ASSERT_UNUSED(type, type == ByHTMLTag); + return adoptRef(*new HTMLTagCollection(rootNode, qualifiedName)); + } + + virtual ~HTMLTagCollection(); + bool elementMatches(Element&) const; + +private: + HTMLTagCollection(ContainerNode& rootNode, const AtomicString& qualifiedName); + + AtomicString m_qualifiedName; + AtomicString m_loweredQualifiedName; +}; + +inline bool TagCollection::elementMatches(Element& element) const +{ + return m_qualifiedName == element.tagQName().toString(); +} + +inline bool TagCollectionNS::elementMatches(Element& element) const +{ + if (m_localName != starAtom && m_localName != element.localName()) + return false; + return m_namespaceURI == starAtom || m_namespaceURI == element.namespaceURI(); +} + +inline bool HTMLTagCollection::elementMatches(Element& element) const +{ + if (element.isHTMLElement()) + return m_loweredQualifiedName == element.tagQName().toString(); + return m_qualifiedName == element.tagQName().toString(); +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/TagNodeList.cpp b/Source/WebCore/dom/TagNodeList.cpp deleted file mode 100644 index 9f4a4f4ba..000000000 --- a/Source/WebCore/dom/TagNodeList.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 1999 Lars Knoll (knoll@kde.org) - * (C) 1999 Antti Koivisto (koivisto@kde.org) - * (C) 2001 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. - * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "config.h" -#include "TagNodeList.h" - -#include "NodeRareData.h" - -namespace WebCore { - -TagNodeList::TagNodeList(ContainerNode& rootNode, Type type, const AtomicString& namespaceURI, const AtomicString& localName) - : LiveNodeList(rootNode, type, DoNotInvalidateOnAttributeChanges) - , m_namespaceURI(namespaceURI) - , m_localName(localName) -{ - ASSERT(m_namespaceURI.isNull() || !m_namespaceURI.isEmpty()); -} - -TagNodeList::~TagNodeList() -{ - if (m_namespaceURI == starAtom) - ownerNode().nodeLists()->removeCacheWithAtomicName(this, m_localName); - else - ownerNode().nodeLists()->removeCacheWithQualifiedName(this, m_namespaceURI, m_localName); -} - -bool TagNodeList::nodeMatches(Element* testNode) const -{ - // Implements http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-getelementsbytagnamens - if (m_localName != starAtom && m_localName != testNode->localName()) - return false; - - return m_namespaceURI == starAtom || m_namespaceURI == testNode->namespaceURI(); -} - -HTMLTagNodeList::HTMLTagNodeList(ContainerNode& rootNode, const AtomicString& localName) - : TagNodeList(rootNode, HTMLTagNodeListType, starAtom, localName) - , m_loweredLocalName(localName.lower()) -{ -} - -bool HTMLTagNodeList::nodeMatches(Element* testNode) const -{ - return nodeMatchesInlined(testNode); -} - -} // namespace WebCore diff --git a/Source/WebCore/dom/TagNodeList.h b/Source/WebCore/dom/TagNodeList.h deleted file mode 100644 index bd098bf80..000000000 --- a/Source/WebCore/dom/TagNodeList.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 1999 Lars Knoll (knoll@kde.org) - * (C) 1999 Antti Koivisto (koivisto@kde.org) - * (C) 2001 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. - * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef TagNodeList_h -#define TagNodeList_h - -#include "Element.h" -#include "LiveNodeList.h" -#include <wtf/text/AtomicString.h> - -namespace WebCore { - -// NodeList that limits to a particular tag. -class TagNodeList : public LiveNodeList { -public: - static PassRefPtr<TagNodeList> create(ContainerNode& rootNode, const AtomicString& namespaceURI, const AtomicString& localName) - { - ASSERT(namespaceURI != starAtom); - return adoptRef(new TagNodeList(rootNode, TagNodeListType, namespaceURI, localName)); - } - - static PassRefPtr<TagNodeList> create(ContainerNode& rootNode, Type type, const AtomicString& localName) - { - ASSERT_UNUSED(type, type == TagNodeListType); - return adoptRef(new TagNodeList(rootNode, TagNodeListType, starAtom, localName)); - } - - virtual ~TagNodeList(); - -protected: - TagNodeList(ContainerNode& rootNode, Type, const AtomicString& namespaceURI, const AtomicString& localName); - - virtual bool nodeMatches(Element*) const override; - - AtomicString m_namespaceURI; - AtomicString m_localName; -}; - -class HTMLTagNodeList : public TagNodeList { -public: - static PassRefPtr<HTMLTagNodeList> create(ContainerNode& rootNode, Type type, const AtomicString& localName) - { - ASSERT_UNUSED(type, type == HTMLTagNodeListType); - return adoptRef(new HTMLTagNodeList(rootNode, localName)); - } - - bool nodeMatchesInlined(Element*) const; - -private: - HTMLTagNodeList(ContainerNode& rootNode, const AtomicString& localName); - - virtual bool nodeMatches(Element*) const override; - - AtomicString m_loweredLocalName; -}; - -inline bool HTMLTagNodeList::nodeMatchesInlined(Element* testNode) const -{ - // Implements http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-getelementsbytagname - if (m_localName != starAtom) { - const AtomicString& localName = testNode->isHTMLElement() ? m_loweredLocalName : m_localName; - if (localName != testNode->localName()) - return false; - } - ASSERT(m_namespaceURI == starAtom); - return true; -} - -} // namespace WebCore - -#endif // TagNodeList_h diff --git a/Source/WebCore/dom/TemplateContentDocumentFragment.h b/Source/WebCore/dom/TemplateContentDocumentFragment.h index 7db57af69..44576e211 100644 --- a/Source/WebCore/dom/TemplateContentDocumentFragment.h +++ b/Source/WebCore/dom/TemplateContentDocumentFragment.h @@ -24,10 +24,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TemplateContentDocumentFragment_h -#define TemplateContentDocumentFragment_h - -#if ENABLE(TEMPLATE_ELEMENT) +#pragma once #include "DocumentFragment.h" @@ -35,28 +32,24 @@ namespace WebCore { class TemplateContentDocumentFragment final : public DocumentFragment { public: - static PassRefPtr<TemplateContentDocumentFragment> create(Document& document, const Element* host) + static Ref<TemplateContentDocumentFragment> create(Document& document, const Element* host) { - return adoptRef(new TemplateContentDocumentFragment(document, host)); + return adoptRef(*new TemplateContentDocumentFragment(document, host)); } const Element* host() const { return m_host; } - void clearHost() { m_host = 0; } + void clearHost() { m_host = nullptr; } private: TemplateContentDocumentFragment(Document& document, const Element* host) - : DocumentFragment(&document, CreateDocumentFragment) + : DocumentFragment(document, CreateDocumentFragment) , m_host(host) { } - virtual bool isTemplateContent() const override { return true; } + bool isTemplateContent() const override { return true; } const Element* m_host; }; } // namespace WebCore - -#endif // ENABLE(TEMPLATE_ELEMENT) - -#endif // TemplateContentDocumentFragment_h diff --git a/Source/WebCore/dom/Text.cpp b/Source/WebCore/dom/Text.cpp index ec140b0af..45f8e27f6 100644 --- a/Source/WebCore/dom/Text.cpp +++ b/Source/WebCore/dom/Text.cpp @@ -22,84 +22,73 @@ #include "config.h" #include "Text.h" +#include "Event.h" +#include "ExceptionCode.h" #include "RenderCombineText.h" +#include "RenderSVGInlineText.h" #include "RenderText.h" +#include "RenderTreeUpdater.h" +#include "SVGElement.h" +#include "SVGNames.h" #include "ScopedEventQueue.h" #include "ShadowRoot.h" -#include "TextNodeTraversal.h" - -#if ENABLE(SVG) -#include "RenderSVGInlineText.h" -#include "SVGNames.h" -#endif - #include "StyleInheritedData.h" #include "StyleResolver.h" +#include "StyleUpdate.h" +#include "TextNodeTraversal.h" #include <wtf/CheckedArithmetic.h> #include <wtf/text/CString.h> #include <wtf/text/StringBuilder.h> namespace WebCore { -PassRefPtr<Text> Text::create(Document& document, const String& data) -{ - return adoptRef(new Text(document, data, CreateText)); -} - -PassRefPtr<Text> Text::create(ScriptExecutionContext& context, const String& data) +Ref<Text> Text::create(Document& document, const String& data) { - return adoptRef(new Text(toDocument(context), data, CreateText)); + return adoptRef(*new Text(document, data, CreateText)); } -PassRefPtr<Text> Text::createEditingText(Document& document, const String& data) +Ref<Text> Text::createEditingText(Document& document, const String& data) { - return adoptRef(new Text(document, data, CreateEditingText)); + return adoptRef(*new Text(document, data, CreateEditingText)); } Text::~Text() { - ASSERT(!renderer()); } -PassRefPtr<Text> Text::splitText(unsigned offset, ExceptionCode& ec) +ExceptionOr<Ref<Text>> Text::splitText(unsigned offset) { - ec = 0; - - // INDEX_SIZE_ERR: Raised if the specified offset is negative or greater than - // the number of 16-bit units in data. - if (offset > length()) { - ec = INDEX_SIZE_ERR; - return 0; - } + if (offset > length()) + return Exception { INDEX_SIZE_ERR }; EventQueueScope scope; - String oldStr = data(); - RefPtr<Text> newText = virtualCreate(oldStr.substring(offset)); - setDataWithoutUpdate(oldStr.substring(0, offset)); + auto oldData = data(); + auto newText = virtualCreate(oldData.substring(offset)); + setDataWithoutUpdate(oldData.substring(0, offset)); - dispatchModifiedEvent(oldStr); + dispatchModifiedEvent(oldData); - if (parentNode()) - parentNode()->insertBefore(newText.get(), nextSibling(), ec); - if (ec) - return 0; + if (auto* parent = parentNode()) { + auto insertResult = parent->insertBefore(newText, nextSibling()); + if (insertResult.hasException()) + return insertResult.releaseException(); + } - if (parentNode()) - document().textNodeSplit(this); + document().textNodeSplit(this); if (renderer()) - renderer()->setTextWithOffset(dataImpl(), 0, oldStr.length()); + renderer()->setTextWithOffset(data(), 0, oldData.length()); - return newText.release(); + return WTFMove(newText); } static const Text* earliestLogicallyAdjacentTextNode(const Text* text) { const Node* node = text; while ((node = node->previousSibling())) { - if (!node->isTextNode()) + if (!is<Text>(*node)) break; - text = toText(node); + text = downcast<Text>(node); } return text; } @@ -108,9 +97,9 @@ static const Text* latestLogicallyAdjacentTextNode(const Text* text) { const Node* node = text; while ((node = node->nextSibling())) { - if (!node->isTextNode()) + if (!is<Text>(*node)) break; - text = toText(node); + text = downcast<Text>(node); } return text; } @@ -119,15 +108,16 @@ String Text::wholeText() const { const Text* startText = earliestLogicallyAdjacentTextNode(this); const Text* endText = latestLogicallyAdjacentTextNode(this); - const Node* onePastEndText = TextNodeTraversal::nextSibling(endText); + ASSERT(endText); + const Node* onePastEndText = TextNodeTraversal::nextSibling(*endText); StringBuilder result; - for (const Text* text = startText; text != onePastEndText; text = TextNodeTraversal::nextSibling(text)) + for (const Text* text = startText; text != onePastEndText; text = TextNodeTraversal::nextSibling(*text)) result.append(text->data()); return result.toString(); } -PassRefPtr<Text> Text::replaceWholeText(const String& newText, ExceptionCode&) +RefPtr<Text> Text::replaceWholeText(const String& newText) { // Remove all adjacent text nodes, and replace the contents of this one. @@ -138,33 +128,33 @@ PassRefPtr<Text> Text::replaceWholeText(const String& newText, ExceptionCode&) RefPtr<Text> protectedThis(this); // Mutation event handlers could cause our last ref to go away RefPtr<ContainerNode> parent = parentNode(); // Protect against mutation handlers moving this node during traversal for (RefPtr<Node> n = startText; n && n != this && n->isTextNode() && n->parentNode() == parent;) { - RefPtr<Node> nodeToRemove(n.release()); + Ref<Node> nodeToRemove(n.releaseNonNull()); n = nodeToRemove->nextSibling(); - parent->removeChild(nodeToRemove.get(), IGNORE_EXCEPTION); + parent->removeChild(nodeToRemove); } if (this != endText) { Node* onePastEndText = endText->nextSibling(); for (RefPtr<Node> n = nextSibling(); n && n != onePastEndText && n->isTextNode() && n->parentNode() == parent;) { - RefPtr<Node> nodeToRemove(n.release()); + Ref<Node> nodeToRemove(n.releaseNonNull()); n = nodeToRemove->nextSibling(); - parent->removeChild(nodeToRemove.get(), IGNORE_EXCEPTION); + parent->removeChild(nodeToRemove); } } if (newText.isEmpty()) { if (parent && parentNode() == parent) - parent->removeChild(this, IGNORE_EXCEPTION); - return 0; + parent->removeChild(*this); + return nullptr; } - setData(newText, IGNORE_EXCEPTION); - return protectedThis.release(); + setData(newText); + return protectedThis; } String Text::nodeName() const { - return textAtom.string(); + return ASCIILiteral("#text"); } Node::NodeType Text::nodeType() const @@ -172,17 +162,16 @@ Node::NodeType Text::nodeType() const return TEXT_NODE; } -PassRefPtr<Node> Text::cloneNode(bool /*deep*/) +Ref<Node> Text::cloneNodeInternal(Document& targetDocument, CloningOperation) { - return create(document(), data()); + return create(targetDocument, data()); } - -#if ENABLE(SVG) static bool isSVGShadowText(Text* text) { Node* parentNode = text->parentNode(); - return parentNode->isShadowRoot() && toShadowRoot(parentNode)->hostElement()->hasTagName(SVGNames::trefTag); + ASSERT(parentNode); + return is<ShadowRoot>(*parentNode) && downcast<ShadowRoot>(*parentNode).host()->hasTagName(SVGNames::trefTag); } static bool isSVGText(Text* text) @@ -190,18 +179,16 @@ static bool isSVGText(Text* text) Node* parentOrShadowHostNode = text->parentOrShadowHostNode(); return parentOrShadowHostNode->isSVGElement() && !parentOrShadowHostNode->hasTagName(SVGNames::foreignObjectTag); } -#endif RenderPtr<RenderText> Text::createTextRenderer(const RenderStyle& style) { -#if ENABLE(SVG) if (isSVGText(this) || isSVGShadowText(this)) - return createRenderer<RenderSVGInlineText>(*this, dataImpl()); -#endif + return createRenderer<RenderSVGInlineText>(*this, data()); + if (style.hasTextCombine()) - return createRenderer<RenderCombineText>(*this, dataImpl()); + return createRenderer<RenderCombineText>(*this, data()); - return createRenderer<RenderText>(*this, dataImpl()); + return createRenderer<RenderText>(*this, data()); } bool Text::childTypeAllowed(NodeType) const @@ -209,25 +196,40 @@ bool Text::childTypeAllowed(NodeType) const return false; } -PassRefPtr<Text> Text::virtualCreate(const String& data) +Ref<Text> Text::virtualCreate(const String& data) { return create(document(), data); } -PassRefPtr<Text> Text::createWithLengthLimit(Document& document, const String& data, unsigned start, unsigned lengthLimit) +Ref<Text> Text::createWithLengthLimit(Document& document, const String& data, unsigned start, unsigned lengthLimit) { unsigned dataLength = data.length(); if (!start && dataLength <= lengthLimit) return create(document, data); - RefPtr<Text> result = Text::create(document, String()); + Ref<Text> result = Text::create(document, String()); result->parserAppendData(data, start, lengthLimit); - return result; } -#ifndef NDEBUG +void Text::updateRendererAfterContentChange(unsigned offsetOfReplacedData, unsigned lengthOfReplacedData) +{ + ASSERT(parentNode()); + if (styleValidity() >= Style::Validity::SubtreeAndRenderersInvalid) + return; + + auto textUpdate = std::make_unique<Style::Update>(document()); + textUpdate->addText(*this); + + RenderTreeUpdater renderTreeUpdater(document()); + renderTreeUpdater.commit(WTFMove(textUpdate)); + + if (auto* renderer = this->renderer()) + renderer->setTextWithOffset(data(), offsetOfReplacedData, lengthOfReplacedData); +} + +#if ENABLE(TREE_DEBUGGING) void Text::formatForDebugger(char* buffer, unsigned length) const { StringBuilder result; @@ -239,11 +241,15 @@ void Text::formatForDebugger(char* buffer, unsigned length) const if (s.length() > 0) { if (result.length()) result.appendLiteral("; "); - result.appendLiteral("value="); + result.appendLiteral("length="); + result.appendNumber(s.length()); + result.appendLiteral("; value=\""); result.append(s); + result.append('"'); } strncpy(buffer, result.toString().utf8().data(), length - 1); + buffer[length - 1] = '\0'; } #endif diff --git a/Source/WebCore/dom/Text.h b/Source/WebCore/dom/Text.h index 186ae6425..187b56f79 100644 --- a/Source/WebCore/dom/Text.h +++ b/Source/WebCore/dom/Text.h @@ -20,8 +20,7 @@ * */ -#ifndef Text_h -#define Text_h +#pragma once #include "CharacterData.h" #include "RenderPtr.h" @@ -29,32 +28,32 @@ namespace WebCore { class RenderText; -class ScriptExecutionContext; class Text : public CharacterData { public: static const unsigned defaultLengthLimit = 1 << 16; - static PassRefPtr<Text> create(Document&, const String&); - static PassRefPtr<Text> create(ScriptExecutionContext&, const String&); - static PassRefPtr<Text> createWithLengthLimit(Document&, const String&, unsigned positionInString, unsigned lengthLimit = defaultLengthLimit); - static PassRefPtr<Text> createEditingText(Document&, const String&); + static Ref<Text> create(Document&, const String&); + static Ref<Text> createWithLengthLimit(Document&, const String&, unsigned positionInString, unsigned lengthLimit = defaultLengthLimit); + static Ref<Text> createEditingText(Document&, const String&); virtual ~Text(); - PassRefPtr<Text> splitText(unsigned offset, ExceptionCode&); + WEBCORE_EXPORT ExceptionOr<Ref<Text>> splitText(unsigned offset); // DOM Level 3: http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-1312295772 - String wholeText() const; - PassRefPtr<Text> replaceWholeText(const String&, ExceptionCode&); + WEBCORE_EXPORT String wholeText() const; + WEBCORE_EXPORT RefPtr<Text> replaceWholeText(const String&); RenderPtr<RenderText> createTextRenderer(const RenderStyle&); - virtual bool canContainRangeEndPoint() const override final { return true; } + bool canContainRangeEndPoint() const final { return true; } RenderText* renderer() const; + void updateRendererAfterContentChange(unsigned offsetOfReplacedData, unsigned lengthOfReplacedData); + protected: Text(Document& document, const String& data, ConstructionType type) : CharacterData(document, data, type) @@ -62,24 +61,20 @@ protected: } private: - virtual String nodeName() const override; - virtual NodeType nodeType() const override; - virtual PassRefPtr<Node> cloneNode(bool deep) override; - virtual bool childTypeAllowed(NodeType) const override; + String nodeName() const override; + NodeType nodeType() const override; + Ref<Node> cloneNodeInternal(Document&, CloningOperation) override; + bool childTypeAllowed(NodeType) const override; - virtual PassRefPtr<Text> virtualCreate(const String&); + virtual Ref<Text> virtualCreate(const String&); -#ifndef NDEBUG - virtual void formatForDebugger(char* buffer, unsigned length) const override; +#if ENABLE(TREE_DEBUGGING) + void formatForDebugger(char* buffer, unsigned length) const override; #endif }; -inline bool isText(const Node& node) { return node.isTextNode(); } -void isText(const Text&); // Catch unnecessary runtime check of type known at compile time. -void isText(const ContainerNode&); // Catch unnecessary runtime check of type known at compile time. - -NODE_TYPE_CASTS(Text) - } // namespace WebCore -#endif // Text_h +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::Text) + static bool isType(const WebCore::Node& node) { return node.isTextNode(); } +SPECIALIZE_TYPE_TRAITS_END() diff --git a/Source/WebCore/dom/Text.idl b/Source/WebCore/dom/Text.idl index 00c9e4765..831224d01 100644 --- a/Source/WebCore/dom/Text.idl +++ b/Source/WebCore/dom/Text.idl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -16,22 +16,15 @@ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ + [ - Constructor([Default=NullString] optional DOMString data), - ConstructorCallWith=ScriptExecutionContext + Constructor(optional DOMString data), + ConstructorCallWith=Document, + CustomToJSObject, ] interface Text : CharacterData { + [MayThrowException] Text splitText(unsigned long offset); - // DOM Level 1 - - [RaisesException] Text splitText([IsIndex,Default=Undefined] optional unsigned long offset); - - // Introduced in DOM Level 3: - readonly attribute DOMString wholeText; - [RaisesException] Text replaceWholeText([Default=Undefined] optional DOMString content); - // ShadowAware API -#if defined(ENABLE_SHADOW_DOM) && ENABLE_SHADOW_DOM && defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT - [ImplementedAs=insertionParentForBinding] readonly attribute Node webkitInsertionParent; -#endif - + readonly attribute DOMString wholeText; }; +Text implements Slotable; diff --git a/Source/WebCore/dom/TextDecoder.cpp b/Source/WebCore/dom/TextDecoder.cpp new file mode 100644 index 000000000..96cc739fc --- /dev/null +++ b/Source/WebCore/dom/TextDecoder.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TextDecoder.h" + +#include "ExceptionCode.h" +#include "HTMLParserIdioms.h" + +namespace WebCore { + +ExceptionOr<Ref<TextDecoder>> TextDecoder::create(const String& label, Options options) +{ + String strippedLabel = stripLeadingAndTrailingHTMLSpaces(label); + const UChar nullCharacter = '\0'; + if (strippedLabel.contains(nullCharacter)) + return Exception { RangeError }; + auto decoder = adoptRef(*new TextDecoder(strippedLabel.utf8().data(), options)); + if (!decoder->m_textEncoding.isValid() || !strcmp(decoder->m_textEncoding.name(), "replacement")) + return Exception { RangeError }; + return WTFMove(decoder); +} + +TextDecoder::TextDecoder(const char* label, Options options) + : m_textEncoding(label) + , m_options(options) +{ +} + +void TextDecoder::ignoreBOMIfNecessary(const uint8_t*& data, size_t& length) +{ + const uint8_t utf8BOMBytes[3] = {0xEF, 0xBB, 0xBF}; + const uint8_t utf16BEBOMBytes[2] = {0xFE, 0xFF}; + const uint8_t utf16LEBOMBytes[2] = {0xFF, 0xFE}; + + if (m_textEncoding == UTF8Encoding() + && length >= sizeof(utf8BOMBytes) + && data[0] == utf8BOMBytes[0] + && data[1] == utf8BOMBytes[1] + && data[2] == utf8BOMBytes[2]) { + data += sizeof(utf8BOMBytes); + length -= sizeof(utf8BOMBytes); + } else if (m_textEncoding == UTF16BigEndianEncoding() + && length >= sizeof(utf16BEBOMBytes) + && data[0] == utf16BEBOMBytes[0] + && data[1] == utf16BEBOMBytes[1]) { + data += sizeof(utf16BEBOMBytes); + length -= sizeof(utf16BEBOMBytes); + } else if (m_textEncoding == UTF16LittleEndianEncoding() + && length >= sizeof(utf16LEBOMBytes) + && data[0] == utf16LEBOMBytes[0] + && data[1] == utf16LEBOMBytes[1]) { + data += sizeof(utf16LEBOMBytes); + length -= sizeof(utf16LEBOMBytes); + } +} + +String TextDecoder::prependBOMIfNecessary(const String& decoded) +{ + if (m_hasDecoded || !m_options.ignoreBOM) + return decoded; + const UChar utf16BEBOM[2] = {0xFEFF, '\0'}; + + // FIXME: Make TextCodec::decode take a flag for prepending BOM so we don't need to do this extra allocation and copy. + return makeString(utf16BEBOM, decoded); +} + +static size_t codeUnitByteSize(const TextEncoding& encoding) +{ + if (encoding.isByteBasedEncoding()) + return 1; + if (encoding == UTF32BigEndianEncoding() || encoding == UTF32LittleEndianEncoding()) + return 4; + return 2; +} + +ExceptionOr<String> TextDecoder::decode(std::optional<BufferSource::VariantType> input, DecodeOptions options) +{ + std::optional<BufferSource> inputBuffer; + const uint8_t* data = nullptr; + size_t length = 0; + if (input) { + inputBuffer = BufferSource(WTFMove(input.value())); + data = inputBuffer->data(); + length = inputBuffer->length(); + } + + ignoreBOMIfNecessary(data, length); + + if (m_buffer.size()) { + m_buffer.append(data, length); + data = m_buffer.data(); + length = m_buffer.size(); + } + + const bool stopOnError = true; + bool sawError = false; + if (length % codeUnitByteSize(m_textEncoding)) + sawError = true; + const char* charData = reinterpret_cast<const char*>(data); + String result; + if (!sawError) + result = prependBOMIfNecessary(m_textEncoding.decode(charData, length, stopOnError, sawError)); + + if (sawError) { + if (options.stream) { + result = String(); + if (!m_buffer.size()) + m_buffer.append(data, length); + } else { + if (m_options.fatal) + return Exception { TypeError }; + result = prependBOMIfNecessary(m_textEncoding.decode(charData, length)); + } + } else + m_buffer.clear(); + + m_hasDecoded = true; + return WTFMove(result); +} + +String TextDecoder::encoding() const +{ + return String(m_textEncoding.name()).convertToASCIILowercase(); +} + +} diff --git a/Source/WebCore/dom/TextDecoder.h b/Source/WebCore/dom/TextDecoder.h new file mode 100644 index 000000000..e4201243a --- /dev/null +++ b/Source/WebCore/dom/TextDecoder.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "BufferSource.h" +#include "ExceptionOr.h" +#include "TextEncoding.h" +#include <wtf/Optional.h> +#include <wtf/Ref.h> +#include <wtf/RefCounted.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class TextDecoder : public RefCounted<TextDecoder> { +public: + struct Options { + bool fatal { false }; + bool ignoreBOM { false }; + }; + struct DecodeOptions { + bool stream { false }; + }; + + static ExceptionOr<Ref<TextDecoder>> create(const String& label, Options); + + String encoding() const; + bool fatal() const { return m_options.fatal; } + bool ignoreBOM() const { return m_options.ignoreBOM; } + ExceptionOr<String> decode(std::optional<BufferSource::VariantType>, DecodeOptions); + +private: + String prependBOMIfNecessary(const String&); + void ignoreBOMIfNecessary(const uint8_t*& data, size_t& length); + TextDecoder(const char*, Options); + TextEncoding m_textEncoding; + Options m_options; + bool m_hasDecoded { false }; + Vector<uint8_t> m_buffer; +}; + +} diff --git a/Source/WebCore/dom/TextDecoder.idl b/Source/WebCore/dom/TextDecoder.idl new file mode 100644 index 000000000..08bb52247 --- /dev/null +++ b/Source/WebCore/dom/TextDecoder.idl @@ -0,0 +1,45 @@ +/* +* Copyright (C) 2016 Apple Inc. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +dictionary TextDecoderOptions { + boolean fatal = false; + boolean ignoreBOM = false; +}; + +dictionary TextDecodeOptions { + boolean stream = false; +}; + +[ + Constructor(optional DOMString label = "utf-8", optional TextDecoderOptions options), + Exposed=(Window,Worker), + ImplementationLacksVTable, + ConstructorMayThrowException +] interface TextDecoder { + readonly attribute DOMString encoding; + readonly attribute boolean fatal; + readonly attribute boolean ignoreBOM; + [MayThrowException] USVString decode(optional BufferSource? input, optional TextDecodeOptions options); +}; diff --git a/Source/WebCore/dom/TextEncoder.cpp b/Source/WebCore/dom/TextEncoder.cpp new file mode 100644 index 000000000..44fb3cfa2 --- /dev/null +++ b/Source/WebCore/dom/TextEncoder.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TextEncoder.h" + +#include <JavaScriptCore/GenericTypedArrayViewInlines.h> +#include <JavaScriptCore/JSGenericTypedArrayViewInlines.h> +#include <runtime/JSCInlines.h> + +namespace WebCore { + +String TextEncoder::encoding() const +{ + return ASCIILiteral("utf-8"); +} + +RefPtr<Uint8Array> TextEncoder::encode(String&& input) const +{ + // FIXME: We should not need to allocate a CString to encode into a Uint8Array. + CString utf8 = input.utf8(); + return Uint8Array::create(reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length()); +} + +} diff --git a/Source/WebCore/dom/TextEncoder.h b/Source/WebCore/dom/TextEncoder.h new file mode 100644 index 000000000..e0274ef88 --- /dev/null +++ b/Source/WebCore/dom/TextEncoder.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <runtime/Uint8Array.h> +#include <wtf/Ref.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class TextEncoder : public RefCounted<TextEncoder> { +public: + static Ref<TextEncoder> create() { return adoptRef(*new TextEncoder); } + String encoding() const; + RefPtr<Uint8Array> encode(String&&) const; +private: + TextEncoder() { }; +}; + +} diff --git a/Source/WebCore/dom/TextEncoder.idl b/Source/WebCore/dom/TextEncoder.idl new file mode 100644 index 000000000..d453fbdbb --- /dev/null +++ b/Source/WebCore/dom/TextEncoder.idl @@ -0,0 +1,35 @@ +/* +* Copyright (C) 2016 Apple Inc. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +[ + Constructor, + Exposed=(Window,Worker), + ImplementationLacksVTable +] interface TextEncoder { + readonly attribute DOMString encoding; + + // FIXME: This should have [NewObject] + Uint8Array encode(optional USVString input = ""); +}; diff --git a/Source/WebCore/dom/TextEvent.cpp b/Source/WebCore/dom/TextEvent.cpp index cdc668de4..9571d364f 100644 --- a/Source/WebCore/dom/TextEvent.cpp +++ b/Source/WebCore/dom/TextEvent.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -28,73 +28,77 @@ #include "TextEvent.h" #include "DocumentFragment.h" +#include "Editor.h" +#include "EventNames.h" namespace WebCore { -PassRefPtr<TextEvent> TextEvent::create() +Ref<TextEvent> TextEvent::createForBindings() { - return adoptRef(new TextEvent); + return adoptRef(*new TextEvent); } -PassRefPtr<TextEvent> TextEvent::create(PassRefPtr<AbstractView> view, const String& data, TextEventInputType inputType) +Ref<TextEvent> TextEvent::create(DOMWindow* view, const String& data, TextEventInputType inputType) { - return adoptRef(new TextEvent(view, data, inputType)); + return adoptRef(*new TextEvent(view, data, inputType)); } -PassRefPtr<TextEvent> TextEvent::createForPlainTextPaste(PassRefPtr<AbstractView> view, const String& data, bool shouldSmartReplace) +Ref<TextEvent> TextEvent::createForPlainTextPaste(DOMWindow* view, const String& data, bool shouldSmartReplace) { - return adoptRef(new TextEvent(view, data, 0, shouldSmartReplace, false)); + return adoptRef(*new TextEvent(view, data, 0, shouldSmartReplace, false, MailBlockquoteHandling::RespectBlockquote)); } -PassRefPtr<TextEvent> TextEvent::createForFragmentPaste(PassRefPtr<AbstractView> view, PassRefPtr<DocumentFragment> data, bool shouldSmartReplace, bool shouldMatchStyle) +Ref<TextEvent> TextEvent::createForFragmentPaste(DOMWindow* view, RefPtr<DocumentFragment>&& data, bool shouldSmartReplace, bool shouldMatchStyle, MailBlockquoteHandling mailBlockquoteHandling) { - return adoptRef(new TextEvent(view, "", data, shouldSmartReplace, shouldMatchStyle)); + return adoptRef(*new TextEvent(view, emptyString(), WTFMove(data), shouldSmartReplace, shouldMatchStyle, mailBlockquoteHandling)); } -PassRefPtr<TextEvent> TextEvent::createForDrop(PassRefPtr<AbstractView> view, const String& data) +Ref<TextEvent> TextEvent::createForDrop(DOMWindow* view, const String& data) { - return adoptRef(new TextEvent(view, data, TextEventInputDrop)); + return adoptRef(*new TextEvent(view, data, TextEventInputDrop)); } -PassRefPtr<TextEvent> TextEvent::createForDictation(PassRefPtr<AbstractView> view, const String& data, const Vector<DictationAlternative>& dictationAlternatives) +Ref<TextEvent> TextEvent::createForDictation(DOMWindow* view, const String& data, const Vector<DictationAlternative>& dictationAlternatives) { - return adoptRef(new TextEvent(view, data, dictationAlternatives)); + return adoptRef(*new TextEvent(view, data, dictationAlternatives)); } TextEvent::TextEvent() : m_inputType(TextEventInputKeyboard) , m_shouldSmartReplace(false) , m_shouldMatchStyle(false) + , m_mailBlockquoteHandling(MailBlockquoteHandling::RespectBlockquote) { } -TextEvent::TextEvent(PassRefPtr<AbstractView> view, const String& data, TextEventInputType inputType) +TextEvent::TextEvent(DOMWindow* view, const String& data, TextEventInputType inputType) : UIEvent(eventNames().textInputEvent, true, true, view, 0) , m_inputType(inputType) , m_data(data) - , m_pastingFragment(0) , m_shouldSmartReplace(false) , m_shouldMatchStyle(false) + , m_mailBlockquoteHandling(MailBlockquoteHandling::RespectBlockquote) { } -TextEvent::TextEvent(PassRefPtr<AbstractView> view, const String& data, PassRefPtr<DocumentFragment> pastingFragment, - bool shouldSmartReplace, bool shouldMatchStyle) +TextEvent::TextEvent(DOMWindow* view, const String& data, RefPtr<DocumentFragment>&& pastingFragment, bool shouldSmartReplace, bool shouldMatchStyle, MailBlockquoteHandling mailBlockquoteHandling) : UIEvent(eventNames().textInputEvent, true, true, view, 0) , m_inputType(TextEventInputPaste) , m_data(data) - , m_pastingFragment(pastingFragment) + , m_pastingFragment(WTFMove(pastingFragment)) , m_shouldSmartReplace(shouldSmartReplace) , m_shouldMatchStyle(shouldMatchStyle) + , m_mailBlockquoteHandling(mailBlockquoteHandling) { } -TextEvent::TextEvent(PassRefPtr<AbstractView> view, const String& data, const Vector<DictationAlternative>& dictationAlternatives) +TextEvent::TextEvent(DOMWindow* view, const String& data, const Vector<DictationAlternative>& dictationAlternatives) : UIEvent(eventNames().textInputEvent, true, true, view, 0) , m_inputType(TextEventInputDictation) , m_data(data) , m_shouldSmartReplace(false) , m_shouldMatchStyle(false) + , m_mailBlockquoteHandling(MailBlockquoteHandling::RespectBlockquote) , m_dictationAlternatives(dictationAlternatives) { } @@ -103,7 +107,7 @@ TextEvent::~TextEvent() { } -void TextEvent::initTextEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<AbstractView> view, const String& data) +void TextEvent::initTextEvent(const AtomicString& type, bool canBubble, bool cancelable, DOMWindow* view, const String& data) { if (dispatched()) return; @@ -118,4 +122,9 @@ EventInterface TextEvent::eventInterface() const return TextEventInterfaceType; } +bool TextEvent::isTextEvent() const +{ + return true; +} + } // namespace WebCore diff --git a/Source/WebCore/dom/TextEvent.h b/Source/WebCore/dom/TextEvent.h index 6cc2cb02c..29eb26d0e 100644 --- a/Source/WebCore/dom/TextEvent.h +++ b/Source/WebCore/dom/TextEvent.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,8 +24,7 @@ * */ -#ifndef TextEvent_h -#define TextEvent_h +#pragma once #include "DictationAlternative.h" #include "TextEventInputType.h" @@ -35,23 +34,24 @@ namespace WebCore { class DocumentFragment; - class TextEvent : public UIEvent { - public: + enum class MailBlockquoteHandling; - static PassRefPtr<TextEvent> create(); - static PassRefPtr<TextEvent> create(PassRefPtr<AbstractView>, const String& data, TextEventInputType = TextEventInputKeyboard); - static PassRefPtr<TextEvent> createForPlainTextPaste(PassRefPtr<AbstractView> view, const String& data, bool shouldSmartReplace); - static PassRefPtr<TextEvent> createForFragmentPaste(PassRefPtr<AbstractView> view, PassRefPtr<DocumentFragment> data, bool shouldSmartReplace, bool shouldMatchStyle); - static PassRefPtr<TextEvent> createForDrop(PassRefPtr<AbstractView> view, const String& data); - static PassRefPtr<TextEvent> createForDictation(PassRefPtr<AbstractView>, const String& data, const Vector<DictationAlternative>& dictationAlternatives); + class TextEvent final : public UIEvent { + public: + static Ref<TextEvent> create(DOMWindow*, const String& data, TextEventInputType = TextEventInputKeyboard); + static Ref<TextEvent> createForBindings(); + static Ref<TextEvent> createForPlainTextPaste(DOMWindow*, const String& data, bool shouldSmartReplace); + static Ref<TextEvent> createForFragmentPaste(DOMWindow*, RefPtr<DocumentFragment>&& data, bool shouldSmartReplace, bool shouldMatchStyle, MailBlockquoteHandling); + static Ref<TextEvent> createForDrop(DOMWindow*, const String& data); + static Ref<TextEvent> createForDictation(DOMWindow*, const String& data, const Vector<DictationAlternative>& dictationAlternatives); virtual ~TextEvent(); - void initTextEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<AbstractView>, const String& data); + WEBCORE_EXPORT void initTextEvent(const AtomicString& type, bool canBubble, bool cancelable, DOMWindow*, const String& data); String data() const { return m_data; } - virtual EventInterface eventInterface() const override; + EventInterface eventInterface() const override; bool isLineBreak() const { return m_inputType == TextEventInputLineBreak; } bool isComposition() const { return m_inputType == TextEventInputComposition; } @@ -59,19 +59,23 @@ namespace WebCore { bool isPaste() const { return m_inputType == TextEventInputPaste; } bool isDrop() const { return m_inputType == TextEventInputDrop; } bool isDictation() const { return m_inputType == TextEventInputDictation; } + bool isAutocompletion() const { return m_inputType == TextEventInputAutocompletion; } + bool isKeyboard() const { return m_inputType == TextEventInputKeyboard; } bool shouldSmartReplace() const { return m_shouldSmartReplace; } bool shouldMatchStyle() const { return m_shouldMatchStyle; } + MailBlockquoteHandling mailBlockquoteHandling() const { return m_mailBlockquoteHandling; } DocumentFragment* pastingFragment() const { return m_pastingFragment.get(); } const Vector<DictationAlternative>& dictationAlternatives() const { return m_dictationAlternatives; } private: TextEvent(); - TextEvent(PassRefPtr<AbstractView>, const String& data, TextEventInputType = TextEventInputKeyboard); - TextEvent(PassRefPtr<AbstractView>, const String& data, PassRefPtr<DocumentFragment>, - bool shouldSmartReplace, bool shouldMatchStyle); - TextEvent(PassRefPtr<AbstractView>, const String& data, const Vector<DictationAlternative>& dictationAlternatives); + TextEvent(DOMWindow*, const String& data, TextEventInputType = TextEventInputKeyboard); + TextEvent(DOMWindow*, const String& data, RefPtr<DocumentFragment>&&, bool shouldSmartReplace, bool shouldMatchStyle, MailBlockquoteHandling); + TextEvent(DOMWindow*, const String& data, const Vector<DictationAlternative>& dictationAlternatives); + + bool isTextEvent() const override; TextEventInputType m_inputType; String m_data; @@ -79,9 +83,10 @@ namespace WebCore { RefPtr<DocumentFragment> m_pastingFragment; bool m_shouldSmartReplace; bool m_shouldMatchStyle; + MailBlockquoteHandling m_mailBlockquoteHandling; Vector<DictationAlternative> m_dictationAlternatives; }; } // namespace WebCore -#endif // TextEvent_h +SPECIALIZE_TYPE_TRAITS_EVENT(TextEvent) diff --git a/Source/WebCore/dom/TextEvent.idl b/Source/WebCore/dom/TextEvent.idl index 4af1a6f4b..364065fa8 100644 --- a/Source/WebCore/dom/TextEvent.idl +++ b/Source/WebCore/dom/TextEvent.idl @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -28,11 +28,12 @@ interface TextEvent : UIEvent { readonly attribute DOMString data; - void initTextEvent([Default=Undefined] optional DOMString typeArg, - [Default=Undefined] optional boolean canBubbleArg, - [Default=Undefined] optional boolean cancelableArg, - [Default=Undefined] optional DOMWindow viewArg, - [Default=Undefined] optional DOMString dataArg); + // FIXME: Using "undefined" as default parameter value is wrong. + void initTextEvent(optional DOMString typeArg = "undefined", + optional boolean canBubbleArg = false, + optional boolean cancelableArg = false, + optional DOMWindow? viewArg = null, + optional DOMString dataArg = "undefined"); }; diff --git a/Source/WebCore/dom/TextEventInputType.h b/Source/WebCore/dom/TextEventInputType.h index 8be233c4b..68eef47bc 100644 --- a/Source/WebCore/dom/TextEventInputType.h +++ b/Source/WebCore/dom/TextEventInputType.h @@ -23,13 +23,13 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TextEventInputType_h -#define TextEventInputType_h +#pragma once namespace WebCore { enum TextEventInputType { TextEventInputKeyboard, // any newline characters in the text are line breaks only, not paragraph separators. + TextEventInputAutocompletion, TextEventInputLineBreak, // any tab characters in the text are backtabs. TextEventInputComposition, TextEventInputBackTab, @@ -40,5 +40,3 @@ enum TextEventInputType { }; } // namespace WebCore - -#endif // TextEventInputType_h diff --git a/Source/WebCore/dom/TextNodeTraversal.cpp b/Source/WebCore/dom/TextNodeTraversal.cpp index 1f30afeca..112a28486 100644 --- a/Source/WebCore/dom/TextNodeTraversal.cpp +++ b/Source/WebCore/dom/TextNodeTraversal.cpp @@ -32,25 +32,25 @@ namespace WebCore { namespace TextNodeTraversal { -void appendContents(const ContainerNode* root, StringBuilder& result) +void appendContents(const ContainerNode& root, StringBuilder& result) { - for (Text* text = TextNodeTraversal::firstWithin(root); text; text = TextNodeTraversal::next(text, root)) + for (Text* text = TextNodeTraversal::firstWithin(root); text; text = TextNodeTraversal::next(*text, &root)) result.append(text->data()); } -String contentsAsString(const ContainerNode* root) +String contentsAsString(const ContainerNode& root) { StringBuilder result; appendContents(root, result); return result.toString(); } -String contentsAsString(const Node* root) +String contentsAsString(const Node& root) { - if (root->isTextNode()) - return toText(root)->data(); - if (root->isContainerNode()) - return contentsAsString(toContainerNode(root)); + if (is<Text>(root)) + return downcast<Text>(root).data(); + if (is<ContainerNode>(root)) + return contentsAsString(downcast<ContainerNode>(root)); return String(); } diff --git a/Source/WebCore/dom/TextNodeTraversal.h b/Source/WebCore/dom/TextNodeTraversal.h index f97493e27..b520e6bfd 100644 --- a/Source/WebCore/dom/TextNodeTraversal.h +++ b/Source/WebCore/dom/TextNodeTraversal.h @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TextNodeTraversal_h -#define TextNodeTraversal_h +#pragma once #include "NodeTraversal.h" #include "Text.h" @@ -37,84 +36,82 @@ namespace WebCore { namespace TextNodeTraversal { // First text child of the node. -Text* firstChild(const Node*); -Text* firstChild(const ContainerNode*); +Text* firstChild(const Node&); +Text* firstChild(const ContainerNode&); // First text descendant of the node. -Text* firstWithin(const Node*); -Text* firstWithin(const ContainerNode*); +Text* firstWithin(const Node&); +Text* firstWithin(const ContainerNode&); // Pre-order traversal skipping non-text nodes. -Text* next(const Node*); -Text* next(const Node*, const Node* stayWithin); -Text* next(const Text*); -Text* next(const Text*, const Node* stayWithin); +Text* next(const Node&); +Text* next(const Node&, const Node* stayWithin); +Text* next(const Text&); +Text* next(const Text&, const Node* stayWithin); // Next text sibling. -Text* nextSibling(const Node*); +Text* nextSibling(const Node&); // Concatenated text contents of a subtree. -String contentsAsString(const Node*); -String contentsAsString(const ContainerNode*); -void appendContents(const ContainerNode*, StringBuilder& result); +String contentsAsString(const Node&); +String contentsAsString(const ContainerNode&); +void appendContents(const ContainerNode&, StringBuilder& result); } namespace TextNodeTraversal { template <class NodeType> -inline Text* firstTextChildTemplate(NodeType* current) +inline Text* firstTextChildTemplate(NodeType& current) { - Node* node = current->firstChild(); - while (node && !node->isTextNode()) + Node* node = current.firstChild(); + while (node && !is<Text>(*node)) node = node->nextSibling(); - return toText(node); + return downcast<Text>(node); } -inline Text* firstChild(const Node* current) { return firstTextChildTemplate(current); } -inline Text* firstChild(const ContainerNode* current) { return firstTextChildTemplate(current); } +inline Text* firstChild(const Node& current) { return firstTextChildTemplate(current); } +inline Text* firstChild(const ContainerNode& current) { return firstTextChildTemplate(current); } template <class NodeType> -inline Text* firstTextWithinTemplate(NodeType* current) +inline Text* firstTextWithinTemplate(NodeType& current) { - Node* node = current->firstChild(); - while (node && !node->isTextNode()) - node = NodeTraversal::next(node, current); - return toText(node); + Node* node = current.firstChild(); + while (node && !is<Text>(*node)) + node = NodeTraversal::next(*node, ¤t); + return downcast<Text>(node); } -inline Text* firstWithin(const Node* current) { return firstTextWithinTemplate(current); } -inline Text* firstWithin(const ContainerNode* current) { return firstTextWithinTemplate(current); } +inline Text* firstWithin(const Node& current) { return firstTextWithinTemplate(current); } +inline Text* firstWithin(const ContainerNode& current) { return firstTextWithinTemplate(current); } template <class NodeType> -inline Text* traverseNextTextTemplate(NodeType* current) +inline Text* traverseNextTextTemplate(NodeType& current) { Node* node = NodeTraversal::next(current); - while (node && !node->isTextNode()) - node = NodeTraversal::next(node); - return toText(node); + while (node && !is<Text>(*node)) + node = NodeTraversal::next(*node); + return downcast<Text>(node); } -inline Text* next(const Node* current) { return traverseNextTextTemplate(current); } -inline Text* next(const Text* current) { return traverseNextTextTemplate(current); } +inline Text* next(const Node& current) { return traverseNextTextTemplate(current); } +inline Text* next(const Text& current) { return traverseNextTextTemplate(current); } template <class NodeType> -inline Text* traverseNextTextTemplate(NodeType* current, const Node* stayWithin) +inline Text* traverseNextTextTemplate(NodeType& current, const Node* stayWithin) { Node* node = NodeTraversal::next(current, stayWithin); - while (node && !node->isTextNode()) - node = NodeTraversal::next(node, stayWithin); - return toText(node); + while (node && !is<Text>(*node)) + node = NodeTraversal::next(*node, stayWithin); + return downcast<Text>(node); } -inline Text* next(const Node* current, const Node* stayWithin) { return traverseNextTextTemplate(current, stayWithin); } -inline Text* next(const Text* current, const Node* stayWithin) { return traverseNextTextTemplate(current, stayWithin); } +inline Text* next(const Node& current, const Node* stayWithin) { return traverseNextTextTemplate(current, stayWithin); } +inline Text* next(const Text& current, const Node* stayWithin) { return traverseNextTextTemplate(current, stayWithin); } -inline Text* nextSibling(const Node* current) +inline Text* nextSibling(const Node& current) { - Node* node = current->nextSibling(); - while (node && !node->isTextNode()) + Node* node = current.nextSibling(); + while (node && !is<Text>(*node)) node = node->nextSibling(); - return toText(node); + return downcast<Text>(node); } -} -} - -#endif +} // namespace TextNodeTraversal +} // namespace WebCore diff --git a/Source/WebCore/dom/Touch.cpp b/Source/WebCore/dom/Touch.cpp index 5ebb7916a..0ce8dd917 100644 --- a/Source/WebCore/dom/Touch.cpp +++ b/Source/WebCore/dom/Touch.cpp @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -70,9 +70,7 @@ Touch::Touch(Frame* frame, EventTarget* target, unsigned identifier, int screenX , m_force(force) { float scaleFactor = frame->pageZoomFactor() * frame->frameScaleFactor(); - float x = pageX * scaleFactor; - float y = pageY * scaleFactor; - m_absoluteLocation = roundedLayoutPoint(FloatPoint(x, y)); + m_absoluteLocation = LayoutPoint(pageX * scaleFactor, pageY * scaleFactor); } Touch::Touch(EventTarget* target, unsigned identifier, int clientX, int clientY, int screenX, int screenY, int pageX, int pageY, int radiusX, int radiusY, float rotationAngle, float force, LayoutPoint absoluteLocation) @@ -92,9 +90,9 @@ Touch::Touch(EventTarget* target, unsigned identifier, int clientX, int clientY, { } -PassRefPtr<Touch> Touch::cloneWithNewTarget(EventTarget* eventTarget) const +Ref<Touch> Touch::cloneWithNewTarget(EventTarget* eventTarget) const { - return adoptRef(new Touch(eventTarget, m_identifier, m_clientX, m_clientY, m_screenX, m_screenY, m_pageX, m_pageY, m_radiusX, m_radiusY, m_rotationAngle, m_force, m_absoluteLocation)); + return adoptRef(*new Touch(eventTarget, m_identifier, m_clientX, m_clientY, m_screenX, m_screenY, m_pageX, m_pageY, m_radiusX, m_radiusY, m_rotationAngle, m_force, m_absoluteLocation)); } } // namespace WebCore diff --git a/Source/WebCore/dom/Touch.h b/Source/WebCore/dom/Touch.h index 7b3d09896..36a12f373 100644 --- a/Source/WebCore/dom/Touch.h +++ b/Source/WebCore/dom/Touch.h @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -23,16 +23,14 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef Touch_h -#define Touch_h +#pragma once -#if PLATFORM(IOS) +#if ENABLE(IOS_TOUCH_EVENTS) #include <WebKitAdditions/TouchIOS.h> #elif ENABLE(TOUCH_EVENTS) #include "EventTarget.h" #include "LayoutPoint.h" -#include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> #include <wtf/RefPtr.h> @@ -42,11 +40,11 @@ class Frame; class Touch : public RefCounted<Touch> { public: - static PassRefPtr<Touch> create(Frame* frame, EventTarget* target, + static Ref<Touch> create(Frame* frame, EventTarget* target, unsigned identifier, int screenX, int screenY, int pageX, int pageY, int radiusX, int radiusY, float rotationAngle, float force) { - return adoptRef(new Touch(frame, target, identifier, screenX, + return adoptRef(*new Touch(frame, target, identifier, screenX, screenY, pageX, pageY, radiusX, radiusY, rotationAngle, force)); } @@ -63,7 +61,7 @@ public: float webkitRotationAngle() const { return m_rotationAngle; } float webkitForce() const { return m_force; } const LayoutPoint& absoluteLocation() const { return m_absoluteLocation; } - PassRefPtr<Touch> cloneWithNewTarget(EventTarget*) const; + Ref<Touch> cloneWithNewTarget(EventTarget*) const; private: Touch(Frame* frame, EventTarget* target, unsigned identifier, @@ -92,5 +90,3 @@ private: } // namespace WebCore #endif // ENABLE(TOUCH_EVENTS) - -#endif /* Touch_h */ diff --git a/Source/WebCore/dom/Touch.idl b/Source/WebCore/dom/Touch.idl index 7486c6f13..5186d7820 100644 --- a/Source/WebCore/dom/Touch.idl +++ b/Source/WebCore/dom/Touch.idl @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -27,16 +27,16 @@ Conditional=TOUCH_EVENTS, ImplementationLacksVTable ] interface Touch { - readonly attribute long clientX; - readonly attribute long clientY; - readonly attribute long screenX; - readonly attribute long screenY; - readonly attribute long pageX; - readonly attribute long pageY; - readonly attribute EventTarget target; - readonly attribute unsigned long identifier; - readonly attribute long webkitRadiusX; - readonly attribute long webkitRadiusY; - readonly attribute float webkitRotationAngle; - readonly attribute float webkitForce; + readonly attribute long clientX; + readonly attribute long clientY; + readonly attribute long screenX; + readonly attribute long screenY; + readonly attribute long pageX; + readonly attribute long pageY; + readonly attribute EventTarget target; + readonly attribute unsigned long identifier; + readonly attribute long webkitRadiusX; + readonly attribute long webkitRadiusY; + readonly attribute unrestricted float webkitRotationAngle; + readonly attribute unrestricted float webkitForce; }; diff --git a/Source/WebCore/dom/TouchEvent.cpp b/Source/WebCore/dom/TouchEvent.cpp index acb2e4dee..f5cec7cc4 100644 --- a/Source/WebCore/dom/TouchEvent.cpp +++ b/Source/WebCore/dom/TouchEvent.cpp @@ -14,7 +14,7 @@ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -41,7 +41,7 @@ TouchEvent::TouchEvent() TouchEvent::TouchEvent(TouchList* touches, TouchList* targetTouches, TouchList* changedTouches, const AtomicString& type, - PassRefPtr<AbstractView> view, int screenX, int screenY, int pageX, int pageY, + DOMWindow* view, int screenX, int screenY, int pageX, int pageY, bool ctrlKey, bool altKey, bool shiftKey, bool metaKey) : MouseRelatedEvent(type, true, true, currentTime(), view, 0, IntPoint(screenX, screenY), IntPoint(pageX, pageY), @@ -55,13 +55,21 @@ TouchEvent::TouchEvent(TouchList* touches, TouchList* targetTouches, { } +TouchEvent::TouchEvent(const AtomicString& type, const Init& initializer, IsTrusted isTrusted) + : MouseRelatedEvent(type, initializer, isTrusted) + , m_touches(initializer.touches ? initializer.touches : TouchList::create()) + , m_targetTouches(initializer.targetTouches ? initializer.targetTouches : TouchList::create()) + , m_changedTouches(initializer.changedTouches ? initializer.changedTouches : TouchList::create()) +{ +} + TouchEvent::~TouchEvent() { } void TouchEvent::initTouchEvent(TouchList* touches, TouchList* targetTouches, TouchList* changedTouches, const AtomicString& type, - PassRefPtr<AbstractView> view, int screenX, int screenY, int clientX, int clientY, + DOMWindow* view, int screenX, int screenY, int clientX, int clientY, bool ctrlKey, bool altKey, bool shiftKey, bool metaKey) { if (dispatched()) diff --git a/Source/WebCore/dom/TouchEvent.h b/Source/WebCore/dom/TouchEvent.h index 1a73ccac5..d339c0b72 100644 --- a/Source/WebCore/dom/TouchEvent.h +++ b/Source/WebCore/dom/TouchEvent.h @@ -14,7 +14,7 @@ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,10 +24,9 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TouchEvent_h -#define TouchEvent_h +#pragma once -#if PLATFORM(IOS) +#if ENABLE(IOS_TOUCH_EVENTS) #include <WebKitAdditions/TouchEventIOS.h> #elif ENABLE(TOUCH_EVENTS) @@ -36,28 +35,39 @@ namespace WebCore { -class TouchEvent : public MouseRelatedEvent { +class TouchEvent final : public MouseRelatedEvent { public: virtual ~TouchEvent(); - static PassRefPtr<TouchEvent> create() - { - return adoptRef(new TouchEvent); - } - static PassRefPtr<TouchEvent> create(TouchList* touches, + static Ref<TouchEvent> create(TouchList* touches, TouchList* targetTouches, TouchList* changedTouches, - const AtomicString& type, PassRefPtr<AbstractView> view, + const AtomicString& type, DOMWindow* view, int screenX, int screenY, int pageX, int pageY, bool ctrlKey, bool altKey, bool shiftKey, bool metaKey) { - return adoptRef(new TouchEvent(touches, targetTouches, changedTouches, + return adoptRef(*new TouchEvent(touches, targetTouches, changedTouches, type, view, screenX, screenY, pageX, pageY, ctrlKey, altKey, shiftKey, metaKey)); } + static Ref<TouchEvent> createForBindings() + { + return adoptRef(*new TouchEvent); + } + + struct Init : MouseRelatedEventInit { + RefPtr<TouchList> touches; + RefPtr<TouchList> targetTouches; + RefPtr<TouchList> changedTouches; + }; + + static Ref<TouchEvent> create(const AtomicString& type, const Init& initializer, IsTrusted isTrusted = IsTrusted::No) + { + return adoptRef(*new TouchEvent(type, initializer, isTrusted)); + } void initTouchEvent(TouchList* touches, TouchList* targetTouches, TouchList* changedTouches, const AtomicString& type, - PassRefPtr<AbstractView> view, int screenX, int screenY, + DOMWindow*, int screenX, int screenY, int clientX, int clientY, bool ctrlKey, bool altKey, bool shiftKey, bool metaKey); @@ -65,41 +75,30 @@ public: TouchList* targetTouches() const { return m_targetTouches.get(); } TouchList* changedTouches() const { return m_changedTouches.get(); } - void setTouches(PassRefPtr<TouchList> touches) { m_touches = touches; } - void setTargetTouches(PassRefPtr<TouchList> targetTouches) { m_targetTouches = targetTouches; } - void setChangedTouches(PassRefPtr<TouchList> changedTouches) { m_changedTouches = changedTouches; } + void setTouches(RefPtr<TouchList>&& touches) { m_touches = touches; } + void setTargetTouches(RefPtr<TouchList>&& targetTouches) { m_targetTouches = targetTouches; } + void setChangedTouches(RefPtr<TouchList>&& changedTouches) { m_changedTouches = changedTouches; } - virtual bool isTouchEvent() const override; + bool isTouchEvent() const override; - virtual EventInterface eventInterface() const; + EventInterface eventInterface() const override; private: TouchEvent(); TouchEvent(TouchList* touches, TouchList* targetTouches, TouchList* changedTouches, const AtomicString& type, - PassRefPtr<AbstractView>, int screenX, int screenY, int pageX, + DOMWindow*, int screenX, int screenY, int pageX, int pageY, bool ctrlKey, bool altKey, bool shiftKey, bool metaKey); + TouchEvent(const AtomicString&, const Init&, IsTrusted); RefPtr<TouchList> m_touches; RefPtr<TouchList> m_targetTouches; RefPtr<TouchList> m_changedTouches; }; -inline TouchEvent* toTouchEvent(Event* event) -{ - ASSERT_WITH_SECURITY_IMPLICATION(event && event->isTouchEvent()); - return static_cast<TouchEvent*>(event); -} - -inline TouchEvent& toTouchEvent(Event& event) -{ - ASSERT(event.isTouchEvent()); - return static_cast<TouchEvent&>(event); -} - } // namespace WebCore -#endif // ENABLE(TOUCH_EVENTS) +SPECIALIZE_TYPE_TRAITS_EVENT(TouchEvent) -#endif // TouchEvent_h +#endif // ENABLE(TOUCH_EVENTS) diff --git a/Source/WebCore/dom/TouchEvent.idl b/Source/WebCore/dom/TouchEvent.idl index 24955938d..81058357e 100644 --- a/Source/WebCore/dom/TouchEvent.idl +++ b/Source/WebCore/dom/TouchEvent.idl @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -25,6 +25,7 @@ [ Conditional=TOUCH_EVENTS, + Constructor(DOMString type, optional TouchEventInit eventInitDict), ] interface TouchEvent : UIEvent { readonly attribute TouchList touches; readonly attribute TouchList targetTouches; @@ -34,17 +35,24 @@ readonly attribute boolean altKey; readonly attribute boolean metaKey; - void initTouchEvent([Default=Undefined] optional TouchList touches, - [Default=Undefined] optional TouchList targetTouches, - [Default=Undefined] optional TouchList changedTouches, - [Default=Undefined] optional DOMString type, - [Default=Undefined] optional DOMWindow view, - [Default=Undefined] optional long screenX, - [Default=Undefined] optional long screenY, - [Default=Undefined] optional long clientX, - [Default=Undefined] optional long clientY, - [Default=Undefined] optional boolean ctrlKey, - [Default=Undefined] optional boolean altKey, - [Default=Undefined] optional boolean shiftKey, - [Default=Undefined] optional boolean metaKey); + // FIXME: Using "undefined" as default parameter value is wrong. + void initTouchEvent(optional TouchList? touches = null, + optional TouchList? targetTouches = null, + optional TouchList? changedTouches = null, + optional DOMString type = "undefined", + optional DOMWindow? view = null, + optional long screenX = 0, + optional long screenY = 0, + optional long clientX = 0, + optional long clientY = 0, + optional boolean ctrlKey = false, + optional boolean altKey = false, + optional boolean shiftKey = false, + optional boolean metaKey = false); +}; + +dictionary TouchEventInit : UIEventInit { + TouchList? touches = null; + TouchList? targetTouches = null; + TouchList? changedTouches = null; }; diff --git a/Source/WebCore/dom/TouchList.cpp b/Source/WebCore/dom/TouchList.cpp index 9add228a1..67b4de832 100644 --- a/Source/WebCore/dom/TouchList.cpp +++ b/Source/WebCore/dom/TouchList.cpp @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -34,8 +34,8 @@ namespace WebCore { Touch* TouchList::item(unsigned index) { if (index >= m_values.size()) - return 0; - return m_values[index].get(); + return nullptr; + return m_values[index].ptr(); } const Touch* TouchList::item(unsigned index) const diff --git a/Source/WebCore/dom/TouchList.h b/Source/WebCore/dom/TouchList.h index b78aa4d02..7336992f3 100644 --- a/Source/WebCore/dom/TouchList.h +++ b/Source/WebCore/dom/TouchList.h @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -22,11 +22,9 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#pragma once -#ifndef TouchList_h -#define TouchList_h - -#if PLATFORM(IOS) +#if ENABLE(IOS_TOUCH_EVENTS) #include <WebKitAdditions/TouchListIOS.h> #elif ENABLE(TOUCH_EVENTS) @@ -38,9 +36,9 @@ namespace WebCore { class TouchList : public RefCounted<TouchList> { public: - static PassRefPtr<TouchList> create() + static Ref<TouchList> create() { - return adoptRef(new TouchList); + return adoptRef(*new TouchList); } unsigned length() const { return m_values.size(); } @@ -48,16 +46,15 @@ public: Touch* item(unsigned); const Touch* item(unsigned) const; - void append(const PassRefPtr<Touch> touch) { m_values.append(touch); } + void append(Ref<Touch>&& touch) { m_values.append(WTFMove(touch)); } private: TouchList() {} - Vector<RefPtr<Touch> > m_values; + Vector<Ref<Touch>> m_values; }; } // namespace WebCore #endif // ENABLE(TOUCH_EVENTS) -#endif /* TouchList_h */ diff --git a/Source/WebCore/dom/TouchList.idl b/Source/WebCore/dom/TouchList.idl index b4047db77..af53e8e95 100644 --- a/Source/WebCore/dom/TouchList.idl +++ b/Source/WebCore/dom/TouchList.idl @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR diff --git a/Source/WebCore/dom/TransformSource.h b/Source/WebCore/dom/TransformSource.h index 78db44e14..a2399a3ad 100644 --- a/Source/WebCore/dom/TransformSource.h +++ b/Source/WebCore/dom/TransformSource.h @@ -17,11 +17,11 @@ * Boston, MA 02110-1301, USA. */ -#ifndef TransformSource_h -#define TransformSource_h +#pragma once #if ENABLE(XSLT) +#include <libxml/tree.h> #include <wtf/FastMalloc.h> #include <wtf/Forward.h> #include <wtf/Noncopyable.h> @@ -29,22 +29,20 @@ namespace WebCore { - typedef void* PlatformTransformSource; +typedef xmlDocPtr PlatformTransformSource; - class TransformSource { - WTF_MAKE_NONCOPYABLE(TransformSource); WTF_MAKE_FAST_ALLOCATED; - public: - explicit TransformSource(const PlatformTransformSource&); - ~TransformSource(); +class TransformSource { + WTF_MAKE_NONCOPYABLE(TransformSource); WTF_MAKE_FAST_ALLOCATED; +public: + explicit TransformSource(const PlatformTransformSource&); + ~TransformSource(); - PlatformTransformSource platformSource() const { return m_source; } + PlatformTransformSource platformSource() const { return m_source; } - private: - PlatformTransformSource m_source; - }; +private: + PlatformTransformSource m_source; +}; } // namespace WebCore -#endif - -#endif // TransformSource_h +#endif // ENABLE(XSLT) diff --git a/Source/WebCore/dom/TransformSourceLibxslt.cpp b/Source/WebCore/dom/TransformSourceLibxslt.cpp index e058438bf..b86e12ab2 100644 --- a/Source/WebCore/dom/TransformSourceLibxslt.cpp +++ b/Source/WebCore/dom/TransformSourceLibxslt.cpp @@ -39,7 +39,7 @@ TransformSource::TransformSource(const PlatformTransformSource& source) TransformSource::~TransformSource() { - xmlFreeDoc((xmlDocPtr)m_source); + xmlFreeDoc(m_source); } } diff --git a/Source/WebCore/dom/TransitionEvent.cpp b/Source/WebCore/dom/TransitionEvent.cpp index de7e0440b..dd06d78e6 100644 --- a/Source/WebCore/dom/TransitionEvent.cpp +++ b/Source/WebCore/dom/TransitionEvent.cpp @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -27,20 +27,8 @@ #include "config.h" #include "TransitionEvent.h" -#include "EventNames.h" - namespace WebCore { -TransitionEventInit::TransitionEventInit() - : elapsedTime(0) -{ -} - -TransitionEvent::TransitionEvent() - : m_elapsedTime(0) -{ -} - TransitionEvent::TransitionEvent(const AtomicString& type, const String& propertyName, double elapsedTime, const String& pseudoElement) : Event(type, true, true) , m_propertyName(propertyName) @@ -49,8 +37,8 @@ TransitionEvent::TransitionEvent(const AtomicString& type, const String& propert { } -TransitionEvent::TransitionEvent(const AtomicString& type, const TransitionEventInit& initializer) - : Event(type, initializer) +TransitionEvent::TransitionEvent(const AtomicString& type, const Init& initializer, IsTrusted isTrusted) + : Event(type, initializer, isTrusted) , m_propertyName(initializer.propertyName) , m_elapsedTime(initializer.elapsedTime) , m_pseudoElement(initializer.pseudoElement) diff --git a/Source/WebCore/dom/TransitionEvent.h b/Source/WebCore/dom/TransitionEvent.h index 748bba8dc..06d1d04f2 100644 --- a/Source/WebCore/dom/TransitionEvent.h +++ b/Source/WebCore/dom/TransitionEvent.h @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,34 +24,28 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TransitionEvent_h -#define TransitionEvent_h +#pragma once #include "Event.h" namespace WebCore { -struct TransitionEventInit : public EventInit { - TransitionEventInit(); - - String propertyName; - double elapsedTime; - String pseudoElement; -}; - -class TransitionEvent : public Event { +class TransitionEvent final : public Event { public: - static PassRefPtr<TransitionEvent> create() - { - return adoptRef(new TransitionEvent); - } - static PassRefPtr<TransitionEvent> create(const AtomicString& type, const String& propertyName, double elapsedTime, const String& pseudoElement) + static Ref<TransitionEvent> create(const AtomicString& type, const String& propertyName, double elapsedTime, const String& pseudoElement) { - return adoptRef(new TransitionEvent(type, propertyName, elapsedTime, pseudoElement)); + return adoptRef(*new TransitionEvent(type, propertyName, elapsedTime, pseudoElement)); } - static PassRefPtr<TransitionEvent> create(const AtomicString& type, const TransitionEventInit& initializer) + + struct Init : EventInit { + String propertyName; + double elapsedTime { 0 }; + String pseudoElement; + }; + + static Ref<TransitionEvent> create(const AtomicString& type, const Init& initializer, IsTrusted isTrusted = IsTrusted::No) { - return adoptRef(new TransitionEvent(type, initializer)); + return adoptRef(*new TransitionEvent(type, initializer, isTrusted)); } virtual ~TransitionEvent(); @@ -60,12 +54,11 @@ public: double elapsedTime() const; const String& pseudoElement() const; - virtual EventInterface eventInterface() const override; + EventInterface eventInterface() const override; private: - TransitionEvent(); TransitionEvent(const AtomicString& type, const String& propertyName, double elapsedTime, const String& pseudoElement); - TransitionEvent(const AtomicString& type, const TransitionEventInit& initializer); + TransitionEvent(const AtomicString& type, const Init& initializer, IsTrusted); String m_propertyName; double m_elapsedTime; @@ -73,6 +66,3 @@ private: }; } // namespace WebCore - -#endif // TransitionEvent_h - diff --git a/Source/WebCore/dom/TransitionEvent.idl b/Source/WebCore/dom/TransitionEvent.idl index afce6608c..03f8ae964 100644 --- a/Source/WebCore/dom/TransitionEvent.idl +++ b/Source/WebCore/dom/TransitionEvent.idl @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -25,10 +25,15 @@ */ [ - ConstructorTemplate=Event + Constructor(DOMString type, optional TransitionEventInit transitionEventInitDict), ] interface TransitionEvent : Event { - [InitializedByEventConstructor] readonly attribute DOMString propertyName; - [InitializedByEventConstructor] readonly attribute double elapsedTime; - [InitializedByEventConstructor] readonly attribute DOMString pseudoElement; + readonly attribute DOMString propertyName; + readonly attribute double elapsedTime; + readonly attribute DOMString pseudoElement; }; +dictionary TransitionEventInit : EventInit { + DOMString propertyName = ""; + double elapsedTime = 0.0; + DOMString pseudoElement = ""; +}; diff --git a/Source/WebCore/dom/Traversal.cpp b/Source/WebCore/dom/Traversal.cpp index a0ec37704..3c82a766e 100644 --- a/Source/WebCore/dom/Traversal.cpp +++ b/Source/WebCore/dom/Traversal.cpp @@ -30,25 +30,22 @@ namespace WebCore { -NodeIteratorBase::NodeIteratorBase(PassRefPtr<Node> rootNode, unsigned whatToShow, PassRefPtr<NodeFilter> nodeFilter, bool expandEntityReferences) +NodeIteratorBase::NodeIteratorBase(Node& rootNode, unsigned whatToShow, RefPtr<NodeFilter>&& nodeFilter) : m_root(rootNode) , m_whatToShow(whatToShow) - , m_filter(nodeFilter) - , m_expandEntityReferences(expandEntityReferences) + , m_filter(WTFMove(nodeFilter)) { } -short NodeIteratorBase::acceptNode(JSC::ExecState* state, Node* node) const +short NodeIteratorBase::acceptNode(Node* node) const { - // FIXME: To handle XML properly we would have to check m_expandEntityReferences. - // The bit twiddling here is done to map DOM node types, which are given as integers from // 1 through 14, to whatToShow bit masks. if (!(((1 << (node->nodeType() - 1)) & m_whatToShow))) return NodeFilter::FILTER_SKIP; if (!m_filter) return NodeFilter::FILTER_ACCEPT; - return m_filter->acceptNode(state, node); + return m_filter->acceptNode(node); } } // namespace WebCore diff --git a/Source/WebCore/dom/Traversal.h b/Source/WebCore/dom/Traversal.h index e980a7da8..c93af8694 100644 --- a/Source/WebCore/dom/Traversal.h +++ b/Source/WebCore/dom/Traversal.h @@ -22,35 +22,31 @@ * */ -#ifndef Traversal_h -#define Traversal_h +#pragma once -#include "ScriptState.h" #include <wtf/RefPtr.h> namespace WebCore { - class Node; - class NodeFilter; +class Node; +class NodeFilter; - class NodeIteratorBase { - public: - Node* root() const { return m_root.get(); } - unsigned whatToShow() const { return m_whatToShow; } - NodeFilter* filter() const { return m_filter.get(); } - bool expandEntityReferences() const { return m_expandEntityReferences; } +class NodeIteratorBase { +public: + Node& root() { return m_root.get(); } + const Node& root() const { return m_root.get(); } - protected: - NodeIteratorBase(PassRefPtr<Node>, unsigned whatToShow, PassRefPtr<NodeFilter>, bool expandEntityReferences); - short acceptNode(JSC::ExecState*, Node*) const; + unsigned whatToShow() const { return m_whatToShow; } + NodeFilter* filter() const { return m_filter.get(); } - private: - RefPtr<Node> m_root; - unsigned m_whatToShow; - RefPtr<NodeFilter> m_filter; - bool m_expandEntityReferences; - }; +protected: + NodeIteratorBase(Node&, unsigned whatToShow, RefPtr<NodeFilter>&&); + short acceptNode(Node*) const; -} // namespace WebCore +private: + Ref<Node> m_root; + unsigned m_whatToShow; + RefPtr<NodeFilter> m_filter; +}; -#endif // Traversal_h +} // namespace WebCore diff --git a/Source/WebCore/dom/TreeDepthLimit.h b/Source/WebCore/dom/TreeDepthLimit.h index e78e093f2..000349ecb 100644 --- a/Source/WebCore/dom/TreeDepthLimit.h +++ b/Source/WebCore/dom/TreeDepthLimit.h @@ -28,13 +28,14 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TreeDepthLimit_h -#define TreeDepthLimit_h +#pragma once #ifndef MAX_DOM_TREE_DEPTH #define MAX_DOM_TREE_DEPTH 5000 #endif -const unsigned maxDOMTreeDepth = MAX_DOM_TREE_DEPTH; -#endif // TreeDepthLimit.h +namespace WebCore { + +const unsigned maxDOMTreeDepth = MAX_DOM_TREE_DEPTH; +} // namespace WebCore diff --git a/Source/WebCore/dom/TreeScope.cpp b/Source/WebCore/dom/TreeScope.cpp index dcd165ec1..cc0e8e3ab 100644 --- a/Source/WebCore/dom/TreeScope.cpp +++ b/Source/WebCore/dom/TreeScope.cpp @@ -27,7 +27,6 @@ #include "config.h" #include "TreeScope.h" -#include "DOMSelection.h" #include "DOMWindow.h" #include "ElementIterator.h" #include "FocusController.h" @@ -40,6 +39,7 @@ #include "HitTestResult.h" #include "IdTargetObserverRegistry.h" #include "Page.h" +#include "PointerLockController.h" #include "RenderView.h" #include "RuntimeEnabledFeatures.h" #include "ShadowRoot.h" @@ -49,98 +49,71 @@ namespace WebCore { struct SameSizeAsTreeScope { - virtual ~SameSizeAsTreeScope(); - void* pointers[9]; - int ints[1]; + void* pointers[8]; }; COMPILE_ASSERT(sizeof(TreeScope) == sizeof(SameSizeAsTreeScope), treescope_should_stay_small); using namespace HTMLNames; -TreeScope::TreeScope(ContainerNode* rootNode, Document* document) - : m_rootNode(rootNode) +TreeScope::TreeScope(ShadowRoot& shadowRoot, Document& document) + : m_rootNode(shadowRoot) , m_documentScope(document) - , m_parentTreeScope(document) - , m_selfOnlyRefCount(0) - , m_idTargetObserverRegistry(IdTargetObserverRegistry::create()) + , m_parentTreeScope(&document) + , m_idTargetObserverRegistry(std::make_unique<IdTargetObserverRegistry>()) { - ASSERT(rootNode); - ASSERT(document); - ASSERT(rootNode != document); - m_parentTreeScope->selfOnlyRef(); - m_rootNode->setTreeScope(*this); + shadowRoot.setTreeScope(*this); } -TreeScope::TreeScope(Document* document) +TreeScope::TreeScope(Document& document) : m_rootNode(document) , m_documentScope(document) , m_parentTreeScope(nullptr) - , m_selfOnlyRefCount(0) - , m_idTargetObserverRegistry(IdTargetObserverRegistry::create()) -{ - ASSERT(document); - m_rootNode->setTreeScope(*this); -} - -TreeScope::TreeScope() - : m_rootNode(nullptr) - , m_documentScope(nullptr) - , m_parentTreeScope(nullptr) - , m_selfOnlyRefCount(0) + , m_idTargetObserverRegistry(std::make_unique<IdTargetObserverRegistry>()) { + document.setTreeScope(*this); } TreeScope::~TreeScope() { - ASSERT(!m_selfOnlyRefCount); - m_rootNode->setTreeScope(noDocumentInstance()); - - if (m_selection) { - m_selection->clearTreeScope(); - m_selection = nullptr; - } - - if (m_parentTreeScope) - m_parentTreeScope->selfOnlyDeref(); } void TreeScope::destroyTreeScopeData() { - m_elementsById.clear(); - m_imageMapsByName.clear(); - m_labelsByForAttribute.clear(); + m_elementsById = nullptr; + m_imageMapsByName = nullptr; + m_labelsByForAttribute = nullptr; } -void TreeScope::clearDocumentScope() -{ - ASSERT(rootNode()->isDocumentNode()); - m_documentScope = nullptr; -} - -void TreeScope::setParentTreeScope(TreeScope* newParentScope) +void TreeScope::setParentTreeScope(TreeScope& newParentScope) { // A document node cannot be re-parented. - ASSERT(!rootNode()->isDocumentNode()); - // Every scope other than document needs a parent scope. - ASSERT(newParentScope); - - newParentScope->selfOnlyRef(); - if (m_parentTreeScope) - m_parentTreeScope->selfOnlyDeref(); - m_parentTreeScope = newParentScope; - setDocumentScope(newParentScope->documentScope()); + ASSERT(!m_rootNode.isDocumentNode()); + + m_parentTreeScope = &newParentScope; + setDocumentScope(newParentScope.documentScope()); } Element* TreeScope::getElementById(const AtomicString& elementId) const { - if (elementId.isEmpty()) + if (elementId.isNull()) return nullptr; if (!m_elementsById) return nullptr; return m_elementsById->getElementById(*elementId.impl(), *this); } +Element* TreeScope::getElementById(const String& elementId) const +{ + if (!m_elementsById) + return nullptr; + + if (RefPtr<AtomicStringImpl> atomicElementId = AtomicStringImpl::lookUp(elementId.impl())) + return m_elementsById->getElementById(*atomicElementId, *this); + + return nullptr; +} + const Vector<Element*>* TreeScope::getAllElementsById(const AtomicString& elementId) const { if (elementId.isEmpty()) @@ -153,7 +126,7 @@ const Vector<Element*>* TreeScope::getAllElementsById(const AtomicString& elemen void TreeScope::addElementById(const AtomicStringImpl& elementId, Element& element, bool notifyObservers) { if (!m_elementsById) - m_elementsById = adoptPtr(new DocumentOrderedMap); + m_elementsById = std::make_unique<DocumentOrderedMap>(); m_elementsById->add(elementId, element, *this); if (notifyObservers) m_idTargetObserverRegistry->notifyObservers(elementId); @@ -180,7 +153,7 @@ Element* TreeScope::getElementByName(const AtomicString& name) const void TreeScope::addElementByName(const AtomicStringImpl& name, Element& element) { if (!m_elementsByName) - m_elementsByName = adoptPtr(new DocumentOrderedMap); + m_elementsByName = std::make_unique<DocumentOrderedMap>(); m_elementsByName->add(name, element, *this); } @@ -191,7 +164,39 @@ void TreeScope::removeElementByName(const AtomicStringImpl& name, Element& eleme m_elementsByName->remove(name, element); } -Node* TreeScope::ancestorInThisScope(Node* node) const + +Node& TreeScope::retargetToScope(Node& node) const +{ + auto& scope = node.treeScope(); + if (LIKELY(this == &scope || !node.isInShadowTree())) + return node; + ASSERT(is<ShadowRoot>(scope.rootNode())); + + Vector<TreeScope*, 8> nodeTreeScopes; + for (auto* currentScope = &scope; currentScope; currentScope = currentScope->parentTreeScope()) + nodeTreeScopes.append(currentScope); + ASSERT(nodeTreeScopes.size() >= 2); + + Vector<const TreeScope*, 8> ancestorScopes; + for (auto* currentScope = this; currentScope; currentScope = currentScope->parentTreeScope()) + ancestorScopes.append(currentScope); + + size_t i = nodeTreeScopes.size(); + size_t j = ancestorScopes.size(); + while (i > 0 && j > 0 && nodeTreeScopes[i - 1] == ancestorScopes[j - 1]) { + --i; + --j; + } + + bool nodeIsInOuterTreeScope = !i; + if (nodeIsInOuterTreeScope) + return node; + + ShadowRoot& shadowRootInLowestCommonTreeScope = downcast<ShadowRoot>(nodeTreeScopes[i - 1]->rootNode()); + return *shadowRootInLowestCommonTreeScope.host(); +} + +Node* TreeScope::ancestorNodeInThisScope(Node* node) const { for (; node; node = node->shadowHost()) { if (&node->treeScope() == this) @@ -202,13 +207,24 @@ Node* TreeScope::ancestorInThisScope(Node* node) const return nullptr; } +Element* TreeScope::ancestorElementInThisScope(Element* element) const +{ + for (; element; element = element->shadowHost()) { + if (&element->treeScope() == this) + return element; + if (!element->isInShadowTree()) + return nullptr; + } + return nullptr; +} + void TreeScope::addImageMap(HTMLMapElement& imageMap) { AtomicStringImpl* name = imageMap.getName().impl(); if (!name) return; if (!m_imageMapsByName) - m_imageMapsByName = adoptPtr(new DocumentOrderedMap); + m_imageMapsByName = std::make_unique<DocumentOrderedMap>(); m_imageMapsByName->add(*name, imageMap, *this); } @@ -224,63 +240,17 @@ void TreeScope::removeImageMap(HTMLMapElement& imageMap) HTMLMapElement* TreeScope::getImageMap(const String& url) const { - if (url.isNull()) - return nullptr; if (!m_imageMapsByName) return nullptr; - size_t hashPos = url.find('#'); - String name = (hashPos == notFound ? url : url.substring(hashPos + 1)).impl(); + auto hashPosition = url.find('#'); + if (hashPosition == notFound) + return nullptr; + String name = url.substring(hashPosition + 1); if (name.isEmpty()) return nullptr; - if (rootNode()->document().isHTMLDocument()) { - AtomicString lowercasedName = name.lower(); - return m_imageMapsByName->getElementByLowercasedMapName(*lowercasedName.impl(), *this); - } return m_imageMapsByName->getElementByMapName(*AtomicString(name).impl(), *this); } -Node* nodeFromPoint(Document* document, int x, int y, LayoutPoint* localPoint) -{ - Frame* frame = document->frame(); - - if (!frame) - return nullptr; - FrameView* frameView = frame->view(); - if (!frameView) - return nullptr; - - float scaleFactor = frame->pageZoomFactor() * frame->frameScaleFactor(); -#if !PLATFORM(IOS) - IntPoint point = roundedIntPoint(FloatPoint(x * scaleFactor + frameView->scrollX(), y * scaleFactor + frameView->scrollY())); - - if (!frameView->visibleContentRect().contains(point)) - return nullptr; -#else - IntPoint point = roundedIntPoint(FloatPoint(x * scaleFactor + frameView->actualScrollX(), y * scaleFactor + frameView->actualScrollY())); - - if (!frameView->actualVisibleContentRect().contains(point)) - return nullptr; -#endif - HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::DisallowShadowContent); - HitTestResult result(point); - document->renderView()->hitTest(request, result); - - if (localPoint) - *localPoint = result.localPoint(); - - return result.innerNode(); -} - -Element* TreeScope::elementFromPoint(int x, int y) const -{ - Node* node = nodeFromPoint(&rootNode()->document(), x, y); - while (node && !node->isElementNode()) - node = node->parentNode(); - if (node) - node = ancestorInThisScope(node); - return toElement(node); -} - void TreeScope::addLabel(const AtomicStringImpl& forAttributeValue, HTMLLabelElement& element) { ASSERT(m_labelsByForAttribute); @@ -300,10 +270,10 @@ HTMLLabelElement* TreeScope::labelElementForId(const AtomicString& forAttributeV if (!m_labelsByForAttribute) { // Populate the map on first access. - m_labelsByForAttribute = adoptPtr(new DocumentOrderedMap); + m_labelsByForAttribute = std::make_unique<DocumentOrderedMap>(); - for (auto& label : descendantsOfType<HTMLLabelElement>(*rootNode())) { - const AtomicString& forValue = label.fastGetAttribute(forAttr); + for (auto& label : descendantsOfType<HTMLLabelElement>(m_rootNode)) { + const AtomicString& forValue = label.attributeWithoutSynchronization(forAttr); if (!forValue.isEmpty()) addLabel(*forValue.impl(), label); } @@ -312,29 +282,56 @@ HTMLLabelElement* TreeScope::labelElementForId(const AtomicString& forAttributeV return m_labelsByForAttribute->getElementByLabelForAttribute(*forAttributeValue.impl(), *this); } -DOMSelection* TreeScope::getSelection() const +Node* TreeScope::nodeFromPoint(const LayoutPoint& clientPoint, LayoutPoint* localPoint) { - if (!rootNode()->document().frame()) + auto* frame = documentScope().frame(); + auto* view = documentScope().view(); + if (!frame || !view) return nullptr; - if (m_selection) - return m_selection.get(); + float scaleFactor = frame->pageZoomFactor() * frame->frameScaleFactor(); - // FIXME: The correct selection in Shadow DOM requires that Position can have a ShadowRoot - // as a container. It is now enabled only if runtime Shadow DOM feature is enabled. - // See https://bugs.webkit.org/show_bug.cgi?id=82697 -#if ENABLE(SHADOW_DOM) - if (RuntimeEnabledFeatures::sharedFeatures().shadowDOMEnabled()) { - m_selection = DOMSelection::create(this); - return m_selection.get(); - } + LayoutPoint contentsPoint = clientPoint; + contentsPoint.scale(scaleFactor); + contentsPoint.moveBy(view->contentsScrollPosition()); + + LayoutRect visibleRect; +#if PLATFORM(IOS) + visibleRect = view->unobscuredContentRect(); +#else + visibleRect = view->visibleContentRect(); #endif + if (!visibleRect.contains(contentsPoint)) + return nullptr; + + HitTestResult result(contentsPoint); + documentScope().renderView()->hitTest(HitTestRequest(), result); + + if (localPoint) + *localPoint = result.localPoint(); + + return result.innerNode(); +} + +Element* TreeScope::elementFromPoint(double x, double y) +{ + Document& document = documentScope(); + if (!document.hasLivingRenderTree()) + return nullptr; - if (this != &rootNode()->document()) - return rootNode()->document().getSelection(); + Node* node = nodeFromPoint(LayoutPoint(x, y), nullptr); + if (!node) + return nullptr; + + node = &retargetToScope(*node); + while (!is<Element>(*node)) { + node = node->parentInComposedTree(); + if (!node) + break; + node = &retargetToScope(*node); + } - m_selection = DOMSelection::create(&rootNode()->document()); - return m_selection.get(); + return downcast<Element>(node); } Element* TreeScope::findAnchor(const String& name) @@ -343,10 +340,12 @@ Element* TreeScope::findAnchor(const String& name) return nullptr; if (Element* element = getElementById(name)) return element; - for (auto& anchor : descendantsOfType<HTMLAnchorElement>(*rootNode())) { - if (rootNode()->document().inQuirksMode()) { - // Quirks mode, case insensitive comparison of names. - if (equalIgnoringCase(anchor.name(), name)) + for (auto& anchor : descendantsOfType<HTMLAnchorElement>(m_rootNode)) { + if (m_rootNode.document().inQuirksMode()) { + // Quirks mode, ASCII case-insensitive comparison of names. + // FIXME: This behavior is not mentioned in the HTML specification. + // We should either remove this or get this into the specification. + if (equalIgnoringASCIICase(anchor.name(), name)) return &anchor; } else { // Strict mode, names need to match exactly. @@ -357,17 +356,10 @@ Element* TreeScope::findAnchor(const String& name) return nullptr; } -bool TreeScope::applyAuthorStyles() const -{ - return true; -} - -void TreeScope::adoptIfNeeded(Node* node) +void TreeScope::adoptIfNeeded(Node& node) { - ASSERT(this); - ASSERT(node); - ASSERT(!node->isDocumentNode()); - ASSERT(!node->m_deletionHasBegun); + ASSERT(!node.isDocumentNode()); + ASSERT(!node.m_deletionHasBegun); TreeScopeAdopter adopter(node, *this); if (adopter.needsScopeChange()) adopter.execute(); @@ -382,25 +374,33 @@ static Element* focusedFrameOwnerElement(Frame* focusedFrame, Frame* currentFram return nullptr; } -Element* TreeScope::focusedElement() +Element* TreeScope::focusedElementInScope() { - Document& document = rootNode()->document(); + Document& document = documentScope(); Element* element = document.focusedElement(); if (!element && document.page()) element = focusedFrameOwnerElement(document.page()->focusController().focusedFrame(), document.frame()); - if (!element) + + return ancestorElementInThisScope(element); +} + +#if ENABLE(POINTER_LOCK) + +Element* TreeScope::pointerLockElement() const +{ + Document& document = documentScope(); + Page* page = document.page(); + if (!page || page->pointerLockController().lockPending()) return nullptr; - TreeScope* treeScope = &element->treeScope(); - while (treeScope != this && treeScope != &document) { - element = toShadowRoot(treeScope->rootNode())->hostElement(); - treeScope = &element->treeScope(); - } - if (this != treeScope) + auto* element = page->pointerLockController().element(); + if (!element || &element->document() != &document) return nullptr; - return element; + return ancestorElementInThisScope(element); } +#endif + static void listTreeScopes(Node* node, Vector<TreeScope*, 5>& treeScopes) { while (true) { @@ -438,24 +438,4 @@ TreeScope* commonTreeScope(Node* nodeA, Node* nodeB) return treeScopesA[indexA] == treeScopesB[indexB] ? treeScopesA[indexA] : nullptr; } -#ifndef NDEBUG -bool TreeScope::deletionHasBegun() -{ - return rootNode() && rootNode()->m_deletionHasBegun; -} - -void TreeScope::beginDeletion() -{ - ASSERT(this != &noDocumentInstance()); - rootNode()->m_deletionHasBegun = true; -} -#endif - -int TreeScope::refCount() const -{ - if (Node* root = rootNode()) - return root->refCount(); - return 0; -} - } // namespace WebCore diff --git a/Source/WebCore/dom/TreeScope.h b/Source/WebCore/dom/TreeScope.h index 4264d74f7..954ec718b 100644 --- a/Source/WebCore/dom/TreeScope.h +++ b/Source/WebCore/dom/TreeScope.h @@ -24,17 +24,16 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TreeScope_h -#define TreeScope_h +#pragma once #include "DocumentOrderedMap.h" +#include <memory> #include <wtf/Forward.h> #include <wtf/text/AtomicString.h> namespace WebCore { class ContainerNode; -class DOMSelection; class Document; class Element; class HTMLLabelElement; @@ -42,49 +41,53 @@ class HTMLMapElement; class LayoutPoint; class IdTargetObserverRegistry; class Node; +class ShadowRoot; -// A class which inherits both Node and TreeScope must call clearRareData() in its destructor -// so that the Node destructor no longer does problematic NodeList cache manipulation in -// the destructor. class TreeScope { friend class Document; friend class TreeScopeAdopter; public: TreeScope* parentTreeScope() const { return m_parentTreeScope; } - void setParentTreeScope(TreeScope*); + void setParentTreeScope(TreeScope&); - Element* focusedElement(); - Element* getElementById(const AtomicString&) const; + Element* focusedElementInScope(); + Element* pointerLockElement() const; + + WEBCORE_EXPORT Element* getElementById(const AtomicString&) const; + WEBCORE_EXPORT Element* getElementById(const String&) const; const Vector<Element*>* getAllElementsById(const AtomicString&) const; bool hasElementWithId(const AtomicStringImpl&) const; bool containsMultipleElementsWithId(const AtomicString& id) const; void addElementById(const AtomicStringImpl& elementId, Element&, bool notifyObservers = true); void removeElementById(const AtomicStringImpl& elementId, Element&, bool notifyObservers = true); - Element* getElementByName(const AtomicString&) const; + WEBCORE_EXPORT Element* getElementByName(const AtomicString&) const; bool hasElementWithName(const AtomicStringImpl&) const; bool containsMultipleElementsWithName(const AtomicString&) const; void addElementByName(const AtomicStringImpl&, Element&); void removeElementByName(const AtomicStringImpl&, Element&); - Document* documentScope() const { return m_documentScope; } + Document& documentScope() const { return m_documentScope.get(); } + static ptrdiff_t documentScopeMemoryOffset() { return OBJECT_OFFSETOF(TreeScope, m_documentScope); } + + // https://dom.spec.whatwg.org/#retarget + Node& retargetToScope(Node&) const; - Node* ancestorInThisScope(Node*) const; + Node* ancestorNodeInThisScope(Node*) const; + WEBCORE_EXPORT Element* ancestorElementInThisScope(Element*) const; void addImageMap(HTMLMapElement&); void removeImageMap(HTMLMapElement&); HTMLMapElement* getImageMap(const String& url) const; - Element* elementFromPoint(int x, int y) const; - // For accessibility. - bool shouldCacheLabelsByForAttribute() const { return m_labelsByForAttribute; } + bool shouldCacheLabelsByForAttribute() const { return !!m_labelsByForAttribute; } void addLabel(const AtomicStringImpl& forAttributeValue, HTMLLabelElement&); void removeLabel(const AtomicStringImpl& forAttributeValue, HTMLLabelElement&); HTMLLabelElement* labelElementForId(const AtomicString& forAttributeValue); - DOMSelection* getSelection() const; + WEBCORE_EXPORT Element* elementFromPoint(double x, double y); // Find first anchor with the given name. // First searches for an element with the given ID, but if that fails, then looks @@ -93,85 +96,37 @@ public: // quirks mode for historical compatibility reasons. Element* findAnchor(const String& name); - virtual bool applyAuthorStyles() const; - // Used by the basic DOM mutation methods (e.g., appendChild()). - void adoptIfNeeded(Node*); + void adoptIfNeeded(Node&); - ContainerNode* rootNode() const { return m_rootNode; } + ContainerNode& rootNode() const { return m_rootNode; } IdTargetObserverRegistry& idTargetObserverRegistry() const { return *m_idTargetObserverRegistry.get(); } - static TreeScope& noDocumentInstance() - { - DEFINE_STATIC_LOCAL(TreeScope, instance, ()); - return instance; - } - - // Nodes belonging to this scope hold self-only references - - // these are enough to keep the scope from being destroyed, but - // not enough to keep it from removing its children. This allows a - // node that outlives its scope to still have a valid document - // pointer without introducing reference cycles. - void selfOnlyRef() - { - ASSERT(!deletionHasBegun()); - ++m_selfOnlyRefCount; - } - - void selfOnlyDeref() - { - ASSERT(!deletionHasBegun()); - --m_selfOnlyRefCount; - if (!m_selfOnlyRefCount && !refCount() && this != &noDocumentInstance()) { - beginDeletion(); - delete this; - } - } - - void removedLastRefToScope(); - protected: - TreeScope(ContainerNode*, Document*); - explicit TreeScope(Document*); - virtual ~TreeScope(); + TreeScope(ShadowRoot&, Document&); + explicit TreeScope(Document&); + ~TreeScope(); void destroyTreeScopeData(); - void clearDocumentScope(); - void setDocumentScope(Document* document) + void setDocumentScope(Document& document) { - ASSERT(document); - ASSERT(this != &noDocumentInstance()); m_documentScope = document; } -private: - TreeScope(); - - virtual void dropChildren() { } - - int refCount() const; -#ifndef NDEBUG - bool deletionHasBegun(); - void beginDeletion(); -#else - bool deletionHasBegun() { return false; } - void beginDeletion() { } -#endif + Node* nodeFromPoint(const LayoutPoint& clientPoint, LayoutPoint* localPoint); - ContainerNode* m_rootNode; - Document* m_documentScope; +private: + ContainerNode& m_rootNode; + std::reference_wrapper<Document> m_documentScope; TreeScope* m_parentTreeScope; - unsigned m_selfOnlyRefCount; - OwnPtr<DocumentOrderedMap> m_elementsById; - OwnPtr<DocumentOrderedMap> m_elementsByName; - OwnPtr<DocumentOrderedMap> m_imageMapsByName; - OwnPtr<DocumentOrderedMap> m_labelsByForAttribute; + std::unique_ptr<DocumentOrderedMap> m_elementsById; + std::unique_ptr<DocumentOrderedMap> m_elementsByName; + std::unique_ptr<DocumentOrderedMap> m_imageMapsByName; + std::unique_ptr<DocumentOrderedMap> m_labelsByForAttribute; - OwnPtr<IdTargetObserverRegistry> m_idTargetObserverRegistry; - - mutable RefPtr<DOMSelection> m_selection; + std::unique_ptr<IdTargetObserverRegistry> m_idTargetObserverRegistry; }; inline bool TreeScope::hasElementWithId(const AtomicStringImpl& id) const @@ -194,9 +149,6 @@ inline bool TreeScope::containsMultipleElementsWithName(const AtomicString& name return m_elementsByName && name.impl() && m_elementsByName->containsMultiple(*name.impl()); } -Node* nodeFromPoint(Document*, int x, int y, LayoutPoint* localPoint = 0); TreeScope* commonTreeScope(Node*, Node*); } // namespace WebCore - -#endif // TreeScope_h diff --git a/Source/WebCore/dom/TreeScopeAdopter.cpp b/Source/WebCore/dom/TreeScopeAdopter.cpp index 3f44fd60e..751d2a813 100644 --- a/Source/WebCore/dom/TreeScopeAdopter.cpp +++ b/Source/WebCore/dom/TreeScopeAdopter.cpp @@ -32,104 +32,105 @@ namespace WebCore { -void TreeScopeAdopter::moveTreeToNewScope(Node* root) const +// FIXME: Do we ever change tree scopes except between documents? +void TreeScopeAdopter::moveTreeToNewScope(Node& root) const { ASSERT(needsScopeChange()); - m_oldScope.selfOnlyRef(); - // If an element is moved from a document and then eventually back again the collection cache for // that element may contain stale data as changes made to it will have updated the DOMTreeVersion // of the document it was moved to. By increasing the DOMTreeVersion of the donating document here // we ensure that the collection cache will be invalidated as needed when the element is moved back. - Document* oldDocument = m_oldScope.documentScope(); - Document* newDocument = m_newScope.documentScope(); - bool willMoveToNewDocument = oldDocument != newDocument; - if (oldDocument && willMoveToNewDocument) - oldDocument->incDOMTreeVersion(); + Document& oldDocument = m_oldScope.documentScope(); + Document& newDocument = m_newScope.documentScope(); + bool willMoveToNewDocument = &oldDocument != &newDocument; + if (willMoveToNewDocument) { + oldDocument.incrementReferencingNodeCount(); + oldDocument.incDOMTreeVersion(); + } - for (Node* node = root; node; node = NodeTraversal::next(node, root)) { - updateTreeScope(node); + for (Node* node = &root; node; node = NodeTraversal::next(*node, &root)) { + updateTreeScope(*node); if (willMoveToNewDocument) - moveNodeToNewDocument(node, oldDocument, newDocument); + moveNodeToNewDocument(*node, oldDocument, newDocument); else if (node->hasRareData()) { NodeRareData* rareData = node->rareData(); if (rareData->nodeLists()) rareData->nodeLists()->adoptTreeScope(); } - if (!node->isElementNode()) + if (!is<Element>(*node)) continue; if (node->hasSyntheticAttrChildNodes()) { - const Vector<RefPtr<Attr>>& attrs = toElement(node)->attrNodeList(); - for (unsigned i = 0; i < attrs.size(); ++i) - moveTreeToNewScope(attrs[i].get()); + for (auto& attr : downcast<Element>(*node).attrNodeList()) + moveTreeToNewScope(*attr); } - if (ShadowRoot* shadow = node->shadowRoot()) { - shadow->setParentTreeScope(&m_newScope); + if (auto* shadow = node->shadowRoot()) { + shadow->setParentTreeScope(m_newScope); if (willMoveToNewDocument) - moveTreeToNewDocument(shadow, oldDocument, newDocument); + moveShadowTreeToNewDocument(*shadow, oldDocument, newDocument); } } - m_oldScope.selfOnlyDeref(); + if (willMoveToNewDocument) + oldDocument.decrementReferencingNodeCount(); } -void TreeScopeAdopter::moveTreeToNewDocument(Node* root, Document* oldDocument, Document* newDocument) const +void TreeScopeAdopter::moveShadowTreeToNewDocument(ShadowRoot& shadowRoot, Document& oldDocument, Document& newDocument) const { - for (Node* node = root; node; node = NodeTraversal::next(node, root)) { - moveNodeToNewDocument(node, oldDocument, newDocument); - if (ShadowRoot* shadow = node->shadowRoot()) - moveTreeToNewDocument(shadow, oldDocument, newDocument); + for (Node* node = &shadowRoot; node; node = NodeTraversal::next(*node, &shadowRoot)) { + moveNodeToNewDocument(*node, oldDocument, newDocument); + if (auto* shadow = node->shadowRoot()) + moveShadowTreeToNewDocument(*shadow, oldDocument, newDocument); } } #ifndef NDEBUG static bool didMoveToNewDocumentWasCalled = false; -static Document* oldDocumentDidMoveToNewDocumentWasCalledWith = 0; +static Document* oldDocumentDidMoveToNewDocumentWasCalledWith = nullptr; -void TreeScopeAdopter::ensureDidMoveToNewDocumentWasCalled(Document* oldDocument) +void TreeScopeAdopter::ensureDidMoveToNewDocumentWasCalled(Document& oldDocument) { ASSERT(!didMoveToNewDocumentWasCalled); - ASSERT_UNUSED(oldDocument, oldDocument == oldDocumentDidMoveToNewDocumentWasCalledWith); + ASSERT_UNUSED(oldDocument, &oldDocument == oldDocumentDidMoveToNewDocumentWasCalledWith); didMoveToNewDocumentWasCalled = true; } #endif -inline void TreeScopeAdopter::updateTreeScope(Node* node) const +inline void TreeScopeAdopter::updateTreeScope(Node& node) const { - ASSERT(!node->isTreeScope()); - ASSERT(&node->treeScope() == &m_oldScope); - m_newScope.selfOnlyRef(); - m_oldScope.selfOnlyDeref(); - node->setTreeScope(m_newScope); + ASSERT(!node.isTreeScope()); + ASSERT(&node.treeScope() == &m_oldScope); + node.setTreeScope(m_newScope); } -inline void TreeScopeAdopter::moveNodeToNewDocument(Node* node, Document* oldDocument, Document* newDocument) const +inline void TreeScopeAdopter::moveNodeToNewDocument(Node& node, Document& oldDocument, Document& newDocument) const { - ASSERT(!node->inDocument() || oldDocument != newDocument); + ASSERT(!node.isConnected() || &oldDocument != &newDocument); + + newDocument.incrementReferencingNodeCount(); + oldDocument.decrementReferencingNodeCount(); - if (node->hasRareData()) { - NodeRareData* rareData = node->rareData(); - if (rareData->nodeLists()) - rareData->nodeLists()->adoptDocument(oldDocument, newDocument); + if (node.hasRareData()) { + NodeRareData* rareData = node.rareData(); + if (auto* nodeLists = rareData->nodeLists()) + nodeLists->adoptDocument(oldDocument, newDocument); } - if (oldDocument) - oldDocument->moveNodeIteratorsToNewDocument(node, newDocument); + oldDocument.moveNodeIteratorsToNewDocument(node, newDocument); - if (node->isShadowRoot()) - toShadowRoot(node)->setDocumentScope(newDocument); + if (is<ShadowRoot>(node)) + downcast<ShadowRoot>(node).setDocumentScope(newDocument); #ifndef NDEBUG didMoveToNewDocumentWasCalled = false; - oldDocumentDidMoveToNewDocumentWasCalledWith = oldDocument; + oldDocumentDidMoveToNewDocumentWasCalledWith = &oldDocument; #endif - node->didMoveToNewDocument(oldDocument); + node.didMoveToNewDocument(oldDocument); ASSERT(didMoveToNewDocumentWasCalled); } diff --git a/Source/WebCore/dom/TreeScopeAdopter.h b/Source/WebCore/dom/TreeScopeAdopter.h index da821d4c0..405dafffc 100644 --- a/Source/WebCore/dom/TreeScopeAdopter.h +++ b/Source/WebCore/dom/TreeScopeAdopter.h @@ -22,8 +22,8 @@ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ -#ifndef TreeScopeAdopter_h -#define TreeScopeAdopter_h + +#pragma once #include "Node.h" @@ -33,35 +33,33 @@ class TreeScope; class TreeScopeAdopter { public: - explicit TreeScopeAdopter(Node* toAdopt, TreeScope& newScope); + explicit TreeScopeAdopter(Node& toAdopt, TreeScope& newScope); void execute() const { moveTreeToNewScope(m_toAdopt); } bool needsScopeChange() const { return &m_oldScope != &m_newScope; } #ifdef NDEBUG - static void ensureDidMoveToNewDocumentWasCalled(Document*) { } + static void ensureDidMoveToNewDocumentWasCalled(Document&) { } #else - static void ensureDidMoveToNewDocumentWasCalled(Document*); + static void ensureDidMoveToNewDocumentWasCalled(Document&); #endif private: - void updateTreeScope(Node*) const; - void moveTreeToNewScope(Node*) const; - void moveTreeToNewDocument(Node*, Document* oldDocument, Document* newDocument) const; - void moveNodeToNewDocument(Node*, Document* oldDocument, Document* newDocument) const; + void updateTreeScope(Node&) const; + void moveTreeToNewScope(Node&) const; + void moveShadowTreeToNewDocument(ShadowRoot&, Document& oldDocument, Document& newDocument) const; + void moveNodeToNewDocument(Node&, Document& oldDocument, Document& newDocument) const; - Node* m_toAdopt; + Node& m_toAdopt; TreeScope& m_newScope; TreeScope& m_oldScope; }; -inline TreeScopeAdopter::TreeScopeAdopter(Node* toAdopt, TreeScope& newScope) +inline TreeScopeAdopter::TreeScopeAdopter(Node& toAdopt, TreeScope& newScope) : m_toAdopt(toAdopt) , m_newScope(newScope) - , m_oldScope(toAdopt->treeScope()) + , m_oldScope(toAdopt.treeScope()) { } -} - -#endif +} // namespace WebCore diff --git a/Source/WebCore/dom/TreeWalker.cpp b/Source/WebCore/dom/TreeWalker.cpp index 172bc41ad..79e7b16ba 100644 --- a/Source/WebCore/dom/TreeWalker.cpp +++ b/Source/WebCore/dom/TreeWalker.cpp @@ -25,59 +25,51 @@ #include "config.h" #include "TreeWalker.h" -#include "ExceptionCode.h" #include "ContainerNode.h" #include "NodeTraversal.h" +#include <runtime/JSCJSValueInlines.h> namespace WebCore { -TreeWalker::TreeWalker(PassRefPtr<Node> rootNode, unsigned whatToShow, PassRefPtr<NodeFilter> filter, bool expandEntityReferences) - : NodeIteratorBase(rootNode, whatToShow, filter, expandEntityReferences) +TreeWalker::TreeWalker(Node& rootNode, unsigned long whatToShow, RefPtr<NodeFilter>&& filter) + : NodeIteratorBase(rootNode, whatToShow, WTFMove(filter)) , m_current(root()) { } -void TreeWalker::setCurrentNode(PassRefPtr<Node> node, ExceptionCode& ec) +void TreeWalker::setCurrentNode(Node& node) { - if (!node) { - ec = NOT_SUPPORTED_ERR; - return; - } m_current = node; } -inline Node* TreeWalker::setCurrent(PassRefPtr<Node> node) +inline Node* TreeWalker::setCurrent(Ref<Node>&& node) { - m_current = node; - return m_current.get(); + m_current = WTFMove(node); + return m_current.ptr(); } -Node* TreeWalker::parentNode(JSC::ExecState* state) +Node* TreeWalker::parentNode() { - RefPtr<Node> node = m_current; - while (node != root()) { + RefPtr<Node> node = m_current.ptr(); + while (node != &root()) { node = node->parentNode(); if (!node) - return 0; - short acceptNodeResult = acceptNode(state, node.get()); - if (state && state->hadException()) - return 0; + return nullptr; + short acceptNodeResult = acceptNode(node.get()); if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) - return setCurrent(node.release()); + return setCurrent(node.releaseNonNull()); } - return 0; + return nullptr; } -Node* TreeWalker::firstChild(JSC::ExecState* state) +Node* TreeWalker::firstChild() { for (RefPtr<Node> node = m_current->firstChild(); node; ) { - short acceptNodeResult = acceptNode(state, node.get()); - if (state && state->hadException()) - return 0; + short acceptNodeResult = acceptNode(node.get()); switch (acceptNodeResult) { case NodeFilter::FILTER_ACCEPT: - m_current = node.release(); - return m_current.get(); + m_current = node.releaseNonNull(); + return m_current.ptr(); case NodeFilter::FILTER_SKIP: if (node->firstChild()) { node = node->firstChild(); @@ -93,24 +85,22 @@ Node* TreeWalker::firstChild(JSC::ExecState* state) break; } ContainerNode* parent = node->parentNode(); - if (!parent || parent == root() || parent == m_current) - return 0; + if (!parent || parent == &root() || parent == m_current.ptr()) + return nullptr; node = parent; } while (node); } - return 0; + return nullptr; } -Node* TreeWalker::lastChild(JSC::ExecState* state) +Node* TreeWalker::lastChild() { for (RefPtr<Node> node = m_current->lastChild(); node; ) { - short acceptNodeResult = acceptNode(state, node.get()); - if (state && state->hadException()) - return 0; + short acceptNodeResult = acceptNode(node.get()); switch (acceptNodeResult) { case NodeFilter::FILTER_ACCEPT: - m_current = node.release(); - return m_current.get(); + m_current = node.releaseNonNull(); + return m_current.ptr(); case NodeFilter::FILTER_SKIP: if (node->lastChild()) { node = node->lastChild(); @@ -126,152 +116,106 @@ Node* TreeWalker::lastChild(JSC::ExecState* state) break; } ContainerNode* parent = node->parentNode(); - if (!parent || parent == root() || parent == m_current) - return 0; + if (!parent || parent == &root() || parent == m_current.ptr()) + return nullptr; node = parent; } while (node); } - return 0; + return nullptr; } -Node* TreeWalker::previousSibling(JSC::ExecState* state) +template<TreeWalker::SiblingTraversalType type> Node* TreeWalker::traverseSiblings() { - RefPtr<Node> node = m_current; - if (node == root()) - return 0; - while (1) { - for (RefPtr<Node> sibling = node->previousSibling(); sibling; ) { - short acceptNodeResult = acceptNode(state, sibling.get()); - if (state && state->hadException()) - return 0; - switch (acceptNodeResult) { - case NodeFilter::FILTER_ACCEPT: - m_current = sibling.release(); - return m_current.get(); - case NodeFilter::FILTER_SKIP: - if (sibling->lastChild()) { - sibling = sibling->lastChild(); - node = sibling; - continue; - } - break; - case NodeFilter::FILTER_REJECT: - break; + RefPtr<Node> node = m_current.ptr(); + if (node == &root()) + return nullptr; + + auto isNext = type == SiblingTraversalType::Next; + while (true) { + for (RefPtr<Node> sibling = isNext ? node->nextSibling() : node->previousSibling(); sibling; ) { + short acceptNodeResult = acceptNode(sibling.get()); + if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) { + m_current = sibling.releaseNonNull(); + return m_current.ptr(); } - sibling = sibling->previousSibling(); + node = sibling; + sibling = isNext ? sibling->firstChild() : sibling->lastChild(); + if (acceptNodeResult == NodeFilter::FILTER_REJECT || !sibling) + sibling = isNext ? node->nextSibling() : node->previousSibling(); } node = node->parentNode(); - if (!node || node == root()) - return 0; - short acceptNodeResult = acceptNode(state, node.get()); - if (state && state->hadException()) - return 0; + if (!node || node == &root()) + return nullptr; + short acceptNodeResult = acceptNode(node.get()); if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) - return 0; + return nullptr; } } -Node* TreeWalker::nextSibling(JSC::ExecState* state) +Node* TreeWalker::previousSibling() { - RefPtr<Node> node = m_current; - if (node == root()) - return 0; - while (1) { - for (RefPtr<Node> sibling = node->nextSibling(); sibling; ) { - short acceptNodeResult = acceptNode(state, sibling.get()); - if (state && state->hadException()) - return 0; - switch (acceptNodeResult) { - case NodeFilter::FILTER_ACCEPT: - m_current = sibling.release(); - return m_current.get(); - case NodeFilter::FILTER_SKIP: - if (sibling->firstChild()) { - sibling = sibling->firstChild(); - node = sibling; - continue; - } - break; - case NodeFilter::FILTER_REJECT: - break; - } - sibling = sibling->nextSibling(); - } - node = node->parentNode(); - if (!node || node == root()) - return 0; - short acceptNodeResult = acceptNode(state, node.get()); - if (state && state->hadException()) - return 0; - if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) - return 0; - } + return traverseSiblings<SiblingTraversalType::Previous>(); +} + +Node* TreeWalker::nextSibling() +{ + return traverseSiblings<SiblingTraversalType::Next>(); } -Node* TreeWalker::previousNode(JSC::ExecState* state) +Node* TreeWalker::previousNode() { - RefPtr<Node> node = m_current; - while (node != root()) { + RefPtr<Node> node = m_current.ptr(); + while (node != &root()) { while (Node* previousSibling = node->previousSibling()) { node = previousSibling; - short acceptNodeResult = acceptNode(state, node.get()); - if (state && state->hadException()) - return 0; + short acceptNodeResult = acceptNode(node.get()); if (acceptNodeResult == NodeFilter::FILTER_REJECT) continue; while (Node* lastChild = node->lastChild()) { node = lastChild; - acceptNodeResult = acceptNode(state, node.get()); - if (state && state->hadException()) - return 0; + acceptNodeResult = acceptNode(node.get()); if (acceptNodeResult == NodeFilter::FILTER_REJECT) break; } if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) { - m_current = node.release(); - return m_current.get(); + m_current = node.releaseNonNull(); + return m_current.ptr(); } } - if (node == root()) - return 0; + if (node == &root()) + return nullptr; ContainerNode* parent = node->parentNode(); if (!parent) - return 0; + return nullptr; node = parent; - short acceptNodeResult = acceptNode(state, node.get()); - if (state && state->hadException()) - return 0; + short acceptNodeResult = acceptNode(node.get()); if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) - return setCurrent(node.release()); + return setCurrent(node.releaseNonNull()); } - return 0; + return nullptr; } -Node* TreeWalker::nextNode(JSC::ExecState* state) +Node* TreeWalker::nextNode() { - RefPtr<Node> node = m_current; + RefPtr<Node> node = m_current.ptr(); Children: while (Node* firstChild = node->firstChild()) { node = firstChild; - short acceptNodeResult = acceptNode(state, node.get()); - if (state && state->hadException()) - return 0; + short acceptNodeResult = acceptNode(node.get()); if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) - return setCurrent(node.release()); + return setCurrent(node.releaseNonNull()); if (acceptNodeResult == NodeFilter::FILTER_REJECT) break; } - while (Node* nextSibling = NodeTraversal::nextSkippingChildren(node.get(), root())) { + while (Node* nextSibling = NodeTraversal::nextSkippingChildren(*node, &root())) { node = nextSibling; - short acceptNodeResult = acceptNode(state, node.get()); - if (state && state->hadException()) - return 0; + short acceptNodeResult = acceptNode(node.get()); if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) - return setCurrent(node.release()); + return setCurrent(node.releaseNonNull()); if (acceptNodeResult == NodeFilter::FILTER_SKIP) goto Children; } - return 0; + return nullptr; } } // namespace WebCore diff --git a/Source/WebCore/dom/TreeWalker.h b/Source/WebCore/dom/TreeWalker.h index 6cf575be6..7270f2df8 100644 --- a/Source/WebCore/dom/TreeWalker.h +++ b/Source/WebCore/dom/TreeWalker.h @@ -22,55 +22,43 @@ * */ -#ifndef TreeWalker_h -#define TreeWalker_h +#pragma once #include "NodeFilter.h" #include "ScriptWrappable.h" #include "Traversal.h" -#include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> namespace WebCore { - typedef int ExceptionCode; - class TreeWalker : public ScriptWrappable, public RefCounted<TreeWalker>, public NodeIteratorBase { public: - static PassRefPtr<TreeWalker> create(PassRefPtr<Node> rootNode, unsigned whatToShow, PassRefPtr<NodeFilter> filter, bool expandEntityReferences) + static Ref<TreeWalker> create(Node& rootNode, unsigned long whatToShow, RefPtr<NodeFilter>&& filter) { - return adoptRef(new TreeWalker(rootNode, whatToShow, filter, expandEntityReferences)); + return adoptRef(*new TreeWalker(rootNode, whatToShow, WTFMove(filter))); } - Node* currentNode() const { return m_current.get(); } - void setCurrentNode(PassRefPtr<Node>, ExceptionCode&); + Node& currentNode() { return m_current.get(); } + const Node& currentNode() const { return m_current.get(); } - Node* parentNode(JSC::ExecState*); - Node* firstChild(JSC::ExecState*); - Node* lastChild(JSC::ExecState*); - Node* previousSibling(JSC::ExecState*); - Node* nextSibling(JSC::ExecState*); - Node* previousNode(JSC::ExecState*); - Node* nextNode(JSC::ExecState*); + WEBCORE_EXPORT void setCurrentNode(Node&); - // Do not call these functions. They are just scaffolding to support the Objective-C bindings. - // They operate in the main thread normal world, and they swallow JS exceptions. - Node* parentNode() { return parentNode(execStateFromNode(mainThreadNormalWorld(), m_current.get())); } - Node* firstChild() { return firstChild(execStateFromNode(mainThreadNormalWorld(), m_current.get())); } - Node* lastChild() { return lastChild(execStateFromNode(mainThreadNormalWorld(), m_current.get())); } - Node* previousSibling() { return previousSibling(execStateFromNode(mainThreadNormalWorld(), m_current.get())); } - Node* nextSibling() { return nextSibling(execStateFromNode(mainThreadNormalWorld(), m_current.get())); } - Node* previousNode() { return previousNode(execStateFromNode(mainThreadNormalWorld(), m_current.get())); } - Node* nextNode() { return nextNode(execStateFromNode(mainThreadNormalWorld(), m_current.get())); } + WEBCORE_EXPORT Node* parentNode(); + WEBCORE_EXPORT Node* firstChild(); + WEBCORE_EXPORT Node* lastChild(); + WEBCORE_EXPORT Node* previousSibling(); + WEBCORE_EXPORT Node* nextSibling(); + WEBCORE_EXPORT Node* previousNode(); + WEBCORE_EXPORT Node* nextNode(); private: - TreeWalker(PassRefPtr<Node>, unsigned whatToShow, PassRefPtr<NodeFilter>, bool expandEntityReferences); + TreeWalker(Node&, unsigned long whatToShow, RefPtr<NodeFilter>&&); + enum class SiblingTraversalType { Previous, Next }; + template<SiblingTraversalType> Node* traverseSiblings(); - Node* setCurrent(PassRefPtr<Node>); + Node* setCurrent(Ref<Node>&&); - RefPtr<Node> m_current; + Ref<Node> m_current; }; } // namespace WebCore - -#endif // TreeWalker_h diff --git a/Source/WebCore/dom/TreeWalker.idl b/Source/WebCore/dom/TreeWalker.idl index b005ec1d2..1cc00ca46 100644 --- a/Source/WebCore/dom/TreeWalker.idl +++ b/Source/WebCore/dom/TreeWalker.idl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006 Apple Inc. * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> * * This library is free software; you can redistribute it and/or @@ -18,23 +18,21 @@ * Boston, MA 02110-1301, USA. */ -// Introduced in DOM Level 2: [ - JSCustomMarkFunction, + ExportToWrappedFunction, ImplementationLacksVTable, + JSCustomMarkFunction, ] interface TreeWalker { readonly attribute Node root; readonly attribute unsigned long whatToShow; readonly attribute NodeFilter filter; - readonly attribute boolean expandEntityReferences; - [SetterRaisesException] attribute Node currentNode; + attribute Node currentNode; - [CallWith=ScriptState] Node parentNode(); - [CallWith=ScriptState] Node firstChild(); - [CallWith=ScriptState] Node lastChild(); - [CallWith=ScriptState] Node previousSibling(); - [CallWith=ScriptState] Node nextSibling(); - [CallWith=ScriptState] Node previousNode(); - [CallWith=ScriptState] Node nextNode(); + Node parentNode(); + Node firstChild(); + Node lastChild(); + Node previousSibling(); + Node nextSibling(); + Node previousNode(); + Node nextNode(); }; - diff --git a/Source/WebCore/dom/TypedElementDescendantIterator.h b/Source/WebCore/dom/TypedElementDescendantIterator.h new file mode 100644 index 000000000..9f35935d9 --- /dev/null +++ b/Source/WebCore/dom/TypedElementDescendantIterator.h @@ -0,0 +1,322 @@ +/* + * Copyright (C) 2013-2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "ElementIterator.h" + +namespace WebCore { + +template<typename ElementType> class DoubleTypedElementDescendantIterator; + +template <typename ElementType> +class TypedElementDescendantIterator : public ElementIterator<ElementType> { +public: + TypedElementDescendantIterator(const ContainerNode& root); + TypedElementDescendantIterator(const ContainerNode& root, ElementType* current); + TypedElementDescendantIterator& operator++(); +}; + +template <typename ElementType> +class TypedElementDescendantConstIterator : public ElementConstIterator<ElementType> { +public: + TypedElementDescendantConstIterator(const ContainerNode& root); + TypedElementDescendantConstIterator(const ContainerNode& root, const ElementType* current); + TypedElementDescendantConstIterator& operator++(); +}; + +template <typename ElementType> +class TypedElementDescendantIteratorAdapter { +public: + TypedElementDescendantIteratorAdapter(ContainerNode& root); + TypedElementDescendantIterator<ElementType> begin(); + TypedElementDescendantIterator<ElementType> end(); + TypedElementDescendantIterator<ElementType> beginAt(ElementType&); + TypedElementDescendantIterator<ElementType> from(Element&); + + ElementType* first(); + ElementType* last(); + +private: + ContainerNode& m_root; +}; + +template <typename ElementType> +class TypedElementDescendantConstIteratorAdapter { +public: + TypedElementDescendantConstIteratorAdapter(const ContainerNode& root); + TypedElementDescendantConstIterator<ElementType> begin() const; + TypedElementDescendantConstIterator<ElementType> end() const; + TypedElementDescendantConstIterator<ElementType> beginAt(const ElementType&) const; + TypedElementDescendantConstIterator<ElementType> from(const Element&) const; + + const ElementType* first() const; + const ElementType* last() const; + +private: + const ContainerNode& m_root; +}; + +template<typename ElementType> class DoubleTypedElementDescendantIteratorAdapter { +public: + typedef TypedElementDescendantIteratorAdapter<ElementType> SingleAdapter; + typedef DoubleTypedElementDescendantIterator<ElementType> Iterator; + + DoubleTypedElementDescendantIteratorAdapter(SingleAdapter&&, SingleAdapter&&); + Iterator begin(); + Iterator end(); + +private: + std::pair<SingleAdapter, SingleAdapter> m_pair; +}; + +template<typename ElementType> class DoubleTypedElementDescendantIterator { +public: + typedef TypedElementDescendantIterator<ElementType> SingleIterator; + typedef std::pair<ElementType&, ElementType&> ReferenceProxy; + + DoubleTypedElementDescendantIterator(SingleIterator&&, SingleIterator&&); + ReferenceProxy operator*() const; + bool operator==(const DoubleTypedElementDescendantIterator&) const; + bool operator!=(const DoubleTypedElementDescendantIterator&) const; + DoubleTypedElementDescendantIterator& operator++(); + +private: + std::pair<SingleIterator, SingleIterator> m_pair; +}; + +template <typename ElementType> TypedElementDescendantIteratorAdapter<ElementType> descendantsOfType(ContainerNode&); +template <typename ElementType> TypedElementDescendantConstIteratorAdapter<ElementType> descendantsOfType(const ContainerNode&); + +// This must only be used when both sets of descendants are known to be the same length. +// If they are different lengths, this will stop when the shorter one reaches the end, but also an assertion will fail. +template<typename ElementType> DoubleTypedElementDescendantIteratorAdapter<ElementType> descendantsOfType(ContainerNode& firstRoot, ContainerNode& secondRoot); + +// TypedElementDescendantIterator + +template <typename ElementType> +inline TypedElementDescendantIterator<ElementType>::TypedElementDescendantIterator(const ContainerNode& root) + : ElementIterator<ElementType>(&root) +{ +} + +template <typename ElementType> +inline TypedElementDescendantIterator<ElementType>::TypedElementDescendantIterator(const ContainerNode& root, ElementType* current) + : ElementIterator<ElementType>(&root, current) +{ +} + +template <typename ElementType> +inline TypedElementDescendantIterator<ElementType>& TypedElementDescendantIterator<ElementType>::operator++() +{ + return static_cast<TypedElementDescendantIterator<ElementType>&>(ElementIterator<ElementType>::traverseNext()); +} + +// TypedElementDescendantConstIterator + +template <typename ElementType> +inline TypedElementDescendantConstIterator<ElementType>::TypedElementDescendantConstIterator(const ContainerNode& root) + : ElementConstIterator<ElementType>(&root) + +{ +} + +template <typename ElementType> +inline TypedElementDescendantConstIterator<ElementType>::TypedElementDescendantConstIterator(const ContainerNode& root, const ElementType* current) + : ElementConstIterator<ElementType>(&root, current) +{ +} + +template <typename ElementType> +inline TypedElementDescendantConstIterator<ElementType>& TypedElementDescendantConstIterator<ElementType>::operator++() +{ + return static_cast<TypedElementDescendantConstIterator<ElementType>&>(ElementConstIterator<ElementType>::traverseNext()); +} + +// TypedElementDescendantIteratorAdapter + +template <typename ElementType> +inline TypedElementDescendantIteratorAdapter<ElementType>::TypedElementDescendantIteratorAdapter(ContainerNode& root) + : m_root(root) +{ +} + +template <typename ElementType> +inline TypedElementDescendantIterator<ElementType> TypedElementDescendantIteratorAdapter<ElementType>::begin() +{ + return TypedElementDescendantIterator<ElementType>(m_root, Traversal<ElementType>::firstWithin(m_root)); +} + +template <typename ElementType> +inline TypedElementDescendantIterator<ElementType> TypedElementDescendantIteratorAdapter<ElementType>::end() +{ + return TypedElementDescendantIterator<ElementType>(m_root); +} + +template <typename ElementType> +inline TypedElementDescendantIterator<ElementType> TypedElementDescendantIteratorAdapter<ElementType>::beginAt(ElementType& descendant) +{ + ASSERT(descendant.isDescendantOf(m_root)); + return TypedElementDescendantIterator<ElementType>(m_root, &descendant); +} + +template <typename ElementType> +inline TypedElementDescendantIterator<ElementType> TypedElementDescendantIteratorAdapter<ElementType>::from(Element& descendant) +{ + ASSERT(descendant.isDescendantOf(m_root)); + if (is<ElementType>(descendant)) + return TypedElementDescendantIterator<ElementType>(m_root, downcast<ElementType>(&descendant)); + ElementType* next = Traversal<ElementType>::next(descendant, &m_root); + return TypedElementDescendantIterator<ElementType>(m_root, next); +} + +template <typename ElementType> +inline ElementType* TypedElementDescendantIteratorAdapter<ElementType>::first() +{ + return Traversal<ElementType>::firstWithin(m_root); +} + +template <typename ElementType> +inline ElementType* TypedElementDescendantIteratorAdapter<ElementType>::last() +{ + return Traversal<ElementType>::lastWithin(m_root); +} + +// TypedElementDescendantConstIteratorAdapter + +template <typename ElementType> +inline TypedElementDescendantConstIteratorAdapter<ElementType>::TypedElementDescendantConstIteratorAdapter(const ContainerNode& root) + : m_root(root) +{ +} + +template <typename ElementType> +inline TypedElementDescendantConstIterator<ElementType> TypedElementDescendantConstIteratorAdapter<ElementType>::begin() const +{ + return TypedElementDescendantConstIterator<ElementType>(m_root, Traversal<ElementType>::firstWithin(m_root)); +} + +template <typename ElementType> +inline TypedElementDescendantConstIterator<ElementType> TypedElementDescendantConstIteratorAdapter<ElementType>::end() const +{ + return TypedElementDescendantConstIterator<ElementType>(m_root); +} + +template <typename ElementType> +inline TypedElementDescendantConstIterator<ElementType> TypedElementDescendantConstIteratorAdapter<ElementType>::beginAt(const ElementType& descendant) const +{ + ASSERT(descendant.isDescendantOf(m_root)); + return TypedElementDescendantConstIterator<ElementType>(m_root, &descendant); +} + +template <typename ElementType> +inline TypedElementDescendantConstIterator<ElementType> TypedElementDescendantConstIteratorAdapter<ElementType>::from(const Element& descendant) const +{ + ASSERT(descendant.isDescendantOf(m_root)); + if (is<ElementType>(descendant)) + return TypedElementDescendantConstIterator<ElementType>(m_root, downcast<ElementType>(&descendant)); + const ElementType* next = Traversal<ElementType>::next(descendant, &m_root); + return TypedElementDescendantConstIterator<ElementType>(m_root, next); +} + +template <typename ElementType> +inline const ElementType* TypedElementDescendantConstIteratorAdapter<ElementType>::first() const +{ + return Traversal<ElementType>::firstWithin(m_root); +} + +template <typename ElementType> +inline const ElementType* TypedElementDescendantConstIteratorAdapter<ElementType>::last() const +{ + return Traversal<ElementType>::lastWithin(m_root); +} + +// DoubleTypedElementDescendantIteratorAdapter + +template<typename ElementType> inline DoubleTypedElementDescendantIteratorAdapter<ElementType>::DoubleTypedElementDescendantIteratorAdapter(SingleAdapter&& first, SingleAdapter&& second) + : m_pair(WTFMove(first), WTFMove(second)) +{ +} + +template<typename ElementType> inline auto DoubleTypedElementDescendantIteratorAdapter<ElementType>::begin() -> Iterator +{ + return Iterator(m_pair.first.begin(), m_pair.second.begin()); +} + +template<typename ElementType> inline auto DoubleTypedElementDescendantIteratorAdapter<ElementType>::end() -> Iterator +{ + return Iterator(m_pair.first.end(), m_pair.second.end()); +} + +// DoubleTypedElementDescendantIterator + +template<typename ElementType> inline DoubleTypedElementDescendantIterator<ElementType>::DoubleTypedElementDescendantIterator(SingleIterator&& first, SingleIterator&& second) + : m_pair(WTFMove(first), WTFMove(second)) +{ +} + +template<typename ElementType> inline auto DoubleTypedElementDescendantIterator<ElementType>::operator*() const -> ReferenceProxy +{ + return { *m_pair.first, *m_pair.second }; +} + +template<typename ElementType> inline bool DoubleTypedElementDescendantIterator<ElementType>::operator==(const DoubleTypedElementDescendantIterator& other) const +{ + ASSERT((m_pair.first == other.m_pair.first) == (m_pair.second == other.m_pair.second)); + return m_pair.first == other.m_pair.first || m_pair.second == other.m_pair.second; +} + +template<typename ElementType> inline bool DoubleTypedElementDescendantIterator<ElementType>::operator!=(const DoubleTypedElementDescendantIterator& other) const +{ + return !(*this == other); +} + +template<typename ElementType> inline DoubleTypedElementDescendantIterator<ElementType>& DoubleTypedElementDescendantIterator<ElementType>::operator++() +{ + ++m_pair.first; + ++m_pair.second; + return *this; +} + +// Standalone functions + +template <typename ElementType> +inline TypedElementDescendantIteratorAdapter<ElementType> descendantsOfType(ContainerNode& root) +{ + return TypedElementDescendantIteratorAdapter<ElementType>(root); +} + +template <typename ElementType> +inline TypedElementDescendantConstIteratorAdapter<ElementType> descendantsOfType(const ContainerNode& root) +{ + return TypedElementDescendantConstIteratorAdapter<ElementType>(root); +} + +template<typename ElementType> inline DoubleTypedElementDescendantIteratorAdapter<ElementType> descendantsOfType(ContainerNode& firstRoot, ContainerNode& secondRoot) +{ + return { descendantsOfType<ElementType>(firstRoot), descendantsOfType<ElementType>(secondRoot) }; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/UIEvent.cpp b/Source/WebCore/dom/UIEvent.cpp index 1ded07933..4cb2239ec 100644 --- a/Source/WebCore/dom/UIEvent.cpp +++ b/Source/WebCore/dom/UIEvent.cpp @@ -23,47 +23,32 @@ #include "config.h" #include "UIEvent.h" -#include "Console.h" #include "Node.h" namespace WebCore { - -UIEventInit::UIEventInit() - : view(0) - , detail(0) -{ -} - -UIEventInit::UIEventInit(bool bubbles, bool cancelable) - : EventInit(bubbles, cancelable) - , view(0) - , detail(0) -{ -} - UIEvent::UIEvent() : m_detail(0) { } -UIEvent::UIEvent(const AtomicString& eventType, bool canBubbleArg, bool cancelableArg, PassRefPtr<AbstractView> viewArg, int detailArg) +UIEvent::UIEvent(const AtomicString& eventType, bool canBubbleArg, bool cancelableArg, DOMWindow* viewArg, int detailArg) : Event(eventType, canBubbleArg, cancelableArg) , m_view(viewArg) , m_detail(detailArg) { } -UIEvent::UIEvent(const AtomicString& eventType, bool canBubbleArg, bool cancelableArg, double timestamp, PassRefPtr<AbstractView> viewArg, int detailArg) +UIEvent::UIEvent(const AtomicString& eventType, bool canBubbleArg, bool cancelableArg, double timestamp, DOMWindow* viewArg, int detailArg) : Event(eventType, canBubbleArg, cancelableArg, timestamp) , m_view(viewArg) , m_detail(detailArg) { } -UIEvent::UIEvent(const AtomicString& eventType, const UIEventInit& initializer) - : Event(eventType, initializer) - , m_view(initializer.view) +UIEvent::UIEvent(const AtomicString& eventType, const UIEventInit& initializer, IsTrusted isTrusted) + : Event(eventType, initializer, isTrusted) + , m_view(initializer.view.get()) , m_detail(initializer.detail) { } @@ -72,7 +57,7 @@ UIEvent::~UIEvent() { } -void UIEvent::initUIEvent(const AtomicString& typeArg, bool canBubbleArg, bool cancelableArg, PassRefPtr<AbstractView> viewArg, int detailArg) +void UIEvent::initUIEvent(const AtomicString& typeArg, bool canBubbleArg, bool cancelableArg, DOMWindow* viewArg, int detailArg) { if (dispatched()) return; @@ -93,16 +78,6 @@ EventInterface UIEvent::eventInterface() const return UIEventInterfaceType; } -int UIEvent::keyCode() const -{ - return 0; -} - -int UIEvent::charCode() const -{ - return 0; -} - int UIEvent::layerX() { return 0; diff --git a/Source/WebCore/dom/UIEvent.h b/Source/WebCore/dom/UIEvent.h index 077bee460..973d73ef3 100644 --- a/Source/WebCore/dom/UIEvent.h +++ b/Source/WebCore/dom/UIEvent.h @@ -21,50 +21,39 @@ * */ -#ifndef UIEvent_h -#define UIEvent_h +#pragma once #include "DOMWindow.h" #include "Event.h" +#include "UIEventInit.h" namespace WebCore { +// FIXME: Remove this when no one is depending on it anymore. typedef DOMWindow AbstractView; -struct UIEventInit : public EventInit { - UIEventInit(); - UIEventInit(bool bubbles, bool cancelable); - - RefPtr<AbstractView> view; - int detail; -}; - class UIEvent : public Event { public: - static PassRefPtr<UIEvent> create() + static Ref<UIEvent> create(const AtomicString& type, bool canBubble, bool cancelable, DOMWindow* view, int detail) { - return adoptRef(new UIEvent); + return adoptRef(*new UIEvent(type, canBubble, cancelable, view, detail)); } - static PassRefPtr<UIEvent> create(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<AbstractView> view, int detail) + static Ref<UIEvent> createForBindings() { - return adoptRef(new UIEvent(type, canBubble, cancelable, view, detail)); + return adoptRef(*new UIEvent); } - static PassRefPtr<UIEvent> create(const AtomicString& type, const UIEventInit& initializer) + static Ref<UIEvent> create(const AtomicString& type, const UIEventInit& initializer, IsTrusted isTrusted = IsTrusted::No) { - return adoptRef(new UIEvent(type, initializer)); + return adoptRef(*new UIEvent(type, initializer, isTrusted)); } virtual ~UIEvent(); - void initUIEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<AbstractView>, int detail); + WEBCORE_EXPORT void initUIEvent(const AtomicString& type, bool canBubble, bool cancelable, DOMWindow*, int detail); - AbstractView* view() const { return m_view.get(); } + DOMWindow* view() const { return m_view.get(); } int detail() const { return m_detail; } - virtual EventInterface eventInterface() const override; - virtual bool isUIEvent() const override; - - virtual int keyCode() const; - virtual int charCode() const; + EventInterface eventInterface() const override; virtual int layerX(); virtual int layerY(); @@ -76,15 +65,17 @@ public: protected: UIEvent(); - UIEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<AbstractView>, int detail); - UIEvent(const AtomicString& type, bool canBubble, bool cancelable, double timestamp, PassRefPtr<AbstractView>, int detail); - UIEvent(const AtomicString&, const UIEventInit&); + UIEvent(const AtomicString& type, bool canBubble, bool cancelable, DOMWindow*, int detail); + UIEvent(const AtomicString& type, bool canBubble, bool cancelable, double timestamp, DOMWindow*, int detail); + UIEvent(const AtomicString&, const UIEventInit&, IsTrusted); private: - RefPtr<AbstractView> m_view; + bool isUIEvent() const final; + + RefPtr<DOMWindow> m_view; int m_detail; }; } // namespace WebCore -#endif // UIEvent_h +SPECIALIZE_TYPE_TRAITS_EVENT(UIEvent) diff --git a/Source/WebCore/dom/UIEvent.idl b/Source/WebCore/dom/UIEvent.idl index 8e724ea43..87b391528 100644 --- a/Source/WebCore/dom/UIEvent.idl +++ b/Source/WebCore/dom/UIEvent.idl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006 Apple Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -18,25 +18,20 @@ */ [ - ConstructorConditional=DOM4_EVENTS_CONSTRUCTOR, - ConstructorTemplate=Event, + Constructor(DOMString type, optional UIEventInit eventInitDict) ] interface UIEvent : Event { - [InitializedByEventConstructor] readonly attribute DOMWindow view; - [InitializedByEventConstructor] readonly attribute long detail; + readonly attribute DOMWindow view; + readonly attribute long detail; - [ObjCLegacyUnnamedParameters] void initUIEvent([Default=Undefined] optional DOMString type, - [Default=Undefined] optional boolean canBubble, - [Default=Undefined] optional boolean cancelable, - [Default=Undefined] optional DOMWindow view, - [Default=Undefined] optional long detail); + // FIXME: Using "undefined" as default parameter value is wrong. + void initUIEvent(optional DOMString type = "undefined", optional boolean canBubble = false, optional boolean cancelable = false, optional DOMWindow? view = null, optional long detail = 0); - // extensions - readonly attribute long keyCode; - readonly attribute long charCode; - readonly attribute long layerX; - readonly attribute long layerY; - readonly attribute long pageX; - readonly attribute long pageY; - readonly attribute long which; -}; + readonly attribute long layerX; + readonly attribute long layerY; + readonly attribute long pageX; + readonly attribute long pageY; + // FIXME: This should be on KeyboardEvent only as per the specification but Firefox and Chrome + // still have this attribute on UIEvent as of December 2016. + readonly attribute long which; +}; diff --git a/Source/WebCore/dom/UIEventInit.h b/Source/WebCore/dom/UIEventInit.h new file mode 100644 index 000000000..0316bc06f --- /dev/null +++ b/Source/WebCore/dom/UIEventInit.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "DOMWindow.h" +#include "EventInit.h" +#include <wtf/RefPtr.h> + +namespace WebCore { + +struct UIEventInit : public EventInit { + RefPtr<DOMWindow> view; + int detail { 0 }; +}; + +} diff --git a/Source/WebCore/dom/UIEventInit.idl b/Source/WebCore/dom/UIEventInit.idl new file mode 100644 index 000000000..0d7eb8efc --- /dev/null +++ b/Source/WebCore/dom/UIEventInit.idl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +dictionary UIEventInit : EventInit { + DOMWindow? view = null; + long detail = 0; +}; diff --git a/Source/WebCore/dom/UIEventWithKeyState.cpp b/Source/WebCore/dom/UIEventWithKeyState.cpp index d757b60a4..a5e1d2525 100644 --- a/Source/WebCore/dom/UIEventWithKeyState.cpp +++ b/Source/WebCore/dom/UIEventWithKeyState.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006 Apple Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public diff --git a/Source/WebCore/dom/UIEventWithKeyState.h b/Source/WebCore/dom/UIEventWithKeyState.h index 5b9ce11ec..bfea387c2 100644 --- a/Source/WebCore/dom/UIEventWithKeyState.h +++ b/Source/WebCore/dom/UIEventWithKeyState.h @@ -21,58 +21,66 @@ * */ -#ifndef UIEventWithKeyState_h -#define UIEventWithKeyState_h +#pragma once +#include "EventModifierInit.h" #include "UIEvent.h" namespace WebCore { - class UIEventWithKeyState : public UIEvent { - public: - bool ctrlKey() const { return m_ctrlKey; } - bool shiftKey() const { return m_shiftKey; } - bool altKey() const { return m_altKey; } - bool metaKey() const { return m_metaKey; } +class UIEventWithKeyState : public UIEvent { +public: + bool ctrlKey() const { return m_ctrlKey; } + bool shiftKey() const { return m_shiftKey; } + bool altKey() const { return m_altKey; } + bool metaKey() const { return m_metaKey; } + bool altGraphKey() const { return m_altGraphKey; } + bool capsLockKey() const { return m_capsLockKey; } - protected: - UIEventWithKeyState() - : m_ctrlKey(false) - , m_altKey(false) - , m_shiftKey(false) - , m_metaKey(false) - { - } +protected: + UIEventWithKeyState() = default; - UIEventWithKeyState(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<AbstractView> view, - int detail, bool ctrlKey, bool altKey, bool shiftKey, bool metaKey) - : UIEvent(type, canBubble, cancelable, view, detail) - , m_ctrlKey(ctrlKey) - , m_altKey(altKey) - , m_shiftKey(shiftKey) - , m_metaKey(metaKey) - { - } + UIEventWithKeyState(const AtomicString& type, bool canBubble, bool cancelable, DOMWindow* view, int detail, bool ctrlKey, bool altKey, bool shiftKey, bool metaKey) + : UIEvent(type, canBubble, cancelable, view, detail) + , m_ctrlKey(ctrlKey) + , m_altKey(altKey) + , m_shiftKey(shiftKey) + , m_metaKey(metaKey) + { + } - UIEventWithKeyState(const AtomicString& type, bool canBubble, bool cancelable, double timestamp, PassRefPtr<AbstractView> view, - int detail, bool ctrlKey, bool altKey, bool shiftKey, bool metaKey) + UIEventWithKeyState(const AtomicString& type, bool canBubble, bool cancelable, double timestamp, DOMWindow* view, + int detail, bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, bool altGraphKey, bool capsLockKey) : UIEvent(type, canBubble, cancelable, timestamp, view, detail) , m_ctrlKey(ctrlKey) , m_altKey(altKey) , m_shiftKey(shiftKey) , m_metaKey(metaKey) - { - } + , m_altGraphKey(altGraphKey) + , m_capsLockKey(capsLockKey) + { + } - // Expose these so init functions can set them. - bool m_ctrlKey : 1; - bool m_altKey : 1; - bool m_shiftKey : 1; - bool m_metaKey : 1; - }; + UIEventWithKeyState(const AtomicString& type, const EventModifierInit& initializer, IsTrusted isTrusted) + : UIEvent(type, initializer, isTrusted) + , m_ctrlKey(initializer.ctrlKey) + , m_altKey(initializer.altKey) + , m_shiftKey(initializer.shiftKey) + , m_metaKey(initializer.metaKey) + , m_altGraphKey(initializer.modifierAltGraph) + , m_capsLockKey(initializer.modifierCapsLock) + { + } - UIEventWithKeyState* findEventWithKeyState(Event*); + // Expose these so init functions can set them. + bool m_ctrlKey { false }; + bool m_altKey { false }; + bool m_shiftKey { false }; + bool m_metaKey { false }; + bool m_altGraphKey { false }; + bool m_capsLockKey { false }; +}; -} // namespace WebCore +WEBCORE_EXPORT UIEventWithKeyState* findEventWithKeyState(Event*); -#endif // UIEventWithKeyState_h +} // namespace WebCore diff --git a/Source/WebCore/dom/UserActionElementSet.cpp b/Source/WebCore/dom/UserActionElementSet.cpp index 98a6e98af..a7dbe5146 100644 --- a/Source/WebCore/dom/UserActionElementSet.cpp +++ b/Source/WebCore/dom/UserActionElementSet.cpp @@ -32,14 +32,6 @@ namespace WebCore { -UserActionElementSet::UserActionElementSet() -{ -} - -UserActionElementSet::~UserActionElementSet() -{ -} - void UserActionElementSet::didDetach(Element* element) { ASSERT(element->isUserActionElement()); diff --git a/Source/WebCore/dom/UserActionElementSet.h b/Source/WebCore/dom/UserActionElementSet.h index 11dd7a011..ce764c3eb 100644 --- a/Source/WebCore/dom/UserActionElementSet.h +++ b/Source/WebCore/dom/UserActionElementSet.h @@ -25,13 +25,9 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef UserActionElementSet_h -#define UserActionElementSet_h +#pragma once #include <wtf/HashMap.h> -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> -#include <wtf/PassRefPtr.h> #include <wtf/RefPtr.h> namespace WebCore { @@ -40,8 +36,6 @@ class Element; class UserActionElementSet { public: - static PassOwnPtr<UserActionElementSet> create() { return adoptPtr(new UserActionElementSet()); } - bool isFocused(const Element* element) { return hasFlags(element, IsFocusedFlag); } bool isActive(const Element* element) { return hasFlags(element, IsActiveFlag); } bool isInActiveChain(const Element* element) { return hasFlags(element, InActiveChainFlag); } @@ -51,9 +45,6 @@ public: void setInActiveChain(Element* element, bool enable) { setFlags(element, enable, InActiveChainFlag); } void setHovered(Element* element, bool enable) { setFlags(element, enable, IsHoveredFlag); } - UserActionElementSet(); - ~UserActionElementSet(); - void didDetach(Element*); void documentDidRemoveLastRef(); @@ -74,6 +65,4 @@ private: ElementFlagMap m_elements; }; -} // namespace - -#endif // UserActionElementSet_h +} // namespace WebCore diff --git a/Source/WebCore/dom/UserGestureIndicator.cpp b/Source/WebCore/dom/UserGestureIndicator.cpp index 5fd20b6c7..09a27a5b2 100644 --- a/Source/WebCore/dom/UserGestureIndicator.cpp +++ b/Source/WebCore/dom/UserGestureIndicator.cpp @@ -26,40 +26,80 @@ #include "config.h" #include "UserGestureIndicator.h" +#include "Document.h" #include <wtf/MainThread.h> +#include <wtf/NeverDestroyed.h> namespace WebCore { -static bool isDefinite(ProcessingUserGestureState state) +static RefPtr<UserGestureToken>& currentToken() { - return state == DefinitelyProcessingUserGesture || state == DefinitelyNotProcessingUserGesture; + static NeverDestroyed<RefPtr<UserGestureToken>> token; + return token; } -ProcessingUserGestureState UserGestureIndicator::s_state = DefinitelyNotProcessingUserGesture; +UserGestureToken::~UserGestureToken() +{ + for (auto& observer : m_destructionObservers) + observer(*this); +} -UserGestureIndicator::UserGestureIndicator(ProcessingUserGestureState state) - : m_previousState(s_state) +UserGestureIndicator::UserGestureIndicator(std::optional<ProcessingUserGestureState> state, Document* document) + : m_previousToken(currentToken()) { // Silently ignore UserGestureIndicators on non main threads. if (!isMainThread()) return; - // We overwrite s_state only if the caller is definite about the gesture state. - if (isDefinite(state)) - s_state = state; - ASSERT(isDefinite(s_state)); + + if (state) + currentToken() = UserGestureToken::create(state.value()); + + if (document && currentToken()->processingUserGesture()) { + document->topDocument().updateLastHandledUserGestureTimestamp(); + document->topDocument().setUserDidInteractWithPage(true); + } +} + +UserGestureIndicator::UserGestureIndicator(RefPtr<UserGestureToken> token) + : m_previousToken(currentToken()) +{ + if (!isMainThread()) + return; + + if (token) + currentToken() = token; } UserGestureIndicator::~UserGestureIndicator() { if (!isMainThread()) return; - s_state = m_previousState; - ASSERT(isDefinite(s_state)); + + currentToken() = m_previousToken; +} + +RefPtr<UserGestureToken> UserGestureIndicator::currentUserGesture() +{ + if (!isMainThread()) + return nullptr; + + return currentToken(); } bool UserGestureIndicator::processingUserGesture() { - return isMainThread() ? s_state == DefinitelyProcessingUserGesture : false; + if (!isMainThread()) + return false; + + return currentToken() ? currentToken()->processingUserGesture() : false; +} + +bool UserGestureIndicator::processingUserGestureForMedia() +{ + if (!isMainThread()) + return false; + + return currentToken() ? currentToken()->processingUserGestureForMedia() : false; } } diff --git a/Source/WebCore/dom/UserGestureIndicator.h b/Source/WebCore/dom/UserGestureIndicator.h index d137c005d..f044f65aa 100644 --- a/Source/WebCore/dom/UserGestureIndicator.h +++ b/Source/WebCore/dom/UserGestureIndicator.h @@ -23,33 +23,68 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef UserGestureIndicator_h -#define UserGestureIndicator_h +#pragma once +#include <wtf/Function.h> #include <wtf/Noncopyable.h> +#include <wtf/Optional.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> namespace WebCore { +class Document; + enum ProcessingUserGestureState { - DefinitelyProcessingUserGesture, - PossiblyProcessingUserGesture, - DefinitelyNotProcessingUserGesture + ProcessingUserGesture, + ProcessingPotentialUserGesture, + NotProcessingUserGesture +}; + +class UserGestureToken : public RefCounted<UserGestureToken> { +public: + static RefPtr<UserGestureToken> create(ProcessingUserGestureState state) + { + return adoptRef(new UserGestureToken(state)); + } + + WEBCORE_EXPORT ~UserGestureToken(); + + ProcessingUserGestureState state() const { return m_state; } + bool processingUserGesture() const { return m_state == ProcessingUserGesture; } + bool processingUserGestureForMedia() const { return m_state == ProcessingUserGesture || m_state == ProcessingPotentialUserGesture; } + + void addDestructionObserver(WTF::Function<void (UserGestureToken&)>&& observer) + { + m_destructionObservers.append(WTFMove(observer)); + } + +private: + UserGestureToken(ProcessingUserGestureState state) + : m_state(state) + { + } + + ProcessingUserGestureState m_state = NotProcessingUserGesture; + Vector<WTF::Function<void (UserGestureToken&)>> m_destructionObservers; }; class UserGestureIndicator { WTF_MAKE_NONCOPYABLE(UserGestureIndicator); public: - static bool processingUserGesture(); + WEBCORE_EXPORT static RefPtr<UserGestureToken> currentUserGesture(); - explicit UserGestureIndicator(ProcessingUserGestureState); - ~UserGestureIndicator(); + WEBCORE_EXPORT static bool processingUserGesture(); + WEBCORE_EXPORT static bool processingUserGestureForMedia(); + // If a document is provided, its last known user gesture timestamp is updated. + WEBCORE_EXPORT explicit UserGestureIndicator(std::optional<ProcessingUserGestureState>, Document* = nullptr); + WEBCORE_EXPORT explicit UserGestureIndicator(RefPtr<UserGestureToken>); + WEBCORE_EXPORT ~UserGestureIndicator(); private: - static ProcessingUserGestureState s_state; - ProcessingUserGestureState m_previousState; + RefPtr<UserGestureToken> m_previousToken; }; } - -#endif diff --git a/Source/WebCore/dom/UserTypingGestureIndicator.cpp b/Source/WebCore/dom/UserTypingGestureIndicator.cpp index aeaabec64..43c61dbab 100644 --- a/Source/WebCore/dom/UserTypingGestureIndicator.cpp +++ b/Source/WebCore/dom/UserTypingGestureIndicator.cpp @@ -54,7 +54,7 @@ UserTypingGestureIndicator::UserTypingGestureIndicator(Frame& frame) , m_previousFocusedNode(focusedNode()) { s_processingUserTypingGesture = true; - focusedNode() = frame.document() ? frame.document()->focusedElement() : 0; + focusedNode() = frame.document() ? frame.document()->focusedElement() : nullptr; } UserTypingGestureIndicator::~UserTypingGestureIndicator() diff --git a/Source/WebCore/dom/UserTypingGestureIndicator.h b/Source/WebCore/dom/UserTypingGestureIndicator.h index d92e95af0..02c2d4c34 100644 --- a/Source/WebCore/dom/UserTypingGestureIndicator.h +++ b/Source/WebCore/dom/UserTypingGestureIndicator.h @@ -23,8 +23,7 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef UserTypingGestureIndicator_h -#define UserTypingGestureIndicator_h +#pragma once #include <wtf/Noncopyable.h> #include <wtf/RefPtr.h> @@ -37,8 +36,8 @@ class Node; class UserTypingGestureIndicator { WTF_MAKE_NONCOPYABLE(UserTypingGestureIndicator); public: - static bool processingUserTypingGesture(); - static Node* focusedElementAtGestureStart(); + WEBCORE_EXPORT static bool processingUserTypingGesture(); + WEBCORE_EXPORT static Node* focusedElementAtGestureStart(); explicit UserTypingGestureIndicator(Frame&); ~UserTypingGestureIndicator(); @@ -49,5 +48,3 @@ private: }; } // namespace WebCore - -#endif // UserTypingGestureIndicator_h diff --git a/Source/WebCore/dom/ViewportArguments.cpp b/Source/WebCore/dom/ViewportArguments.cpp index 2c9539daa..1e69b926d 100644 --- a/Source/WebCore/dom/ViewportArguments.cpp +++ b/Source/WebCore/dom/ViewportArguments.cpp @@ -28,18 +28,11 @@ #include "config.h" #include "ViewportArguments.h" -#include "Chrome.h" -#include "Console.h" -#include "DOMWindow.h" #include "Document.h" #include "Frame.h" #include "IntSize.h" -#include "Page.h" #include "ScriptableDocumentParser.h" - -#if PLATFORM(IOS) -#include "WebCoreSystemInterface.h" -#endif +#include "TextStream.h" namespace WebCore { @@ -93,7 +86,6 @@ ViewportAttributes ViewportArguments::resolve(const FloatSize& initialViewportSi float resultZoom = zoom; float resultMinZoom = minZoom; float resultMaxZoom = maxZoom; - float resultUserZoom = userZoom; switch (int(resultWidth)) { case ViewportArguments::ValueDeviceWidth: @@ -251,8 +243,9 @@ ViewportAttributes ViewportArguments::resolve(const FloatSize& initialViewportSi // if (resultZoom == ViewportArguments::ValueAuto) // result.initialScale = ViewportArguments::ValueAuto; - result.userScalable = resultUserZoom; + result.userScalable = userZoom; result.orientation = orientation; + result.shrinkToFit = shrinkToFit; return result; } @@ -267,13 +260,6 @@ static FloatSize convertToUserSpace(const FloatSize& deviceSize, float devicePix ViewportAttributes computeViewportAttributes(ViewportArguments args, int desktopWidth, int deviceWidth, int deviceHeight, float devicePixelRatio, IntSize visibleViewport) { -#if PLATFORM(IOS) - // FIXME: This should probably be fixed elsewhere on iOS. iOS may only use computeViewportAttributes for tests. - CGSize screenSize = wkGetViewportScreenSize(); - visibleViewport.setWidth(screenSize.width); - visibleViewport.setHeight(screenSize.height); -#endif - FloatSize initialViewportSize = convertToUserSpace(visibleViewport, devicePixelRatio); FloatSize deviceSize = convertToUserSpace(FloatSize(deviceWidth, deviceHeight), devicePixelRatio); @@ -299,48 +285,57 @@ void restrictScaleFactorToInitialScaleIfNotUserScalable(ViewportAttributes& resu result.maximumScale = result.minimumScale = result.initialScale; } -static float numericPrefix(const String& keyString, const String& valueString, Document* document, bool* ok = 0) +static void reportViewportWarning(Document&, ViewportErrorCode, StringView replacement1 = { }, StringView replacement2 = { }); + +static float numericPrefix(Document& document, StringView key, StringView value, bool* ok = nullptr) { size_t parsedLength; - float value; - if (valueString.is8Bit()) - value = charactersToFloat(valueString.characters8(), valueString.length(), parsedLength); + float numericValue; + if (value.is8Bit()) + numericValue = charactersToFloat(value.characters8(), value.length(), parsedLength); else - value = charactersToFloat(valueString.characters16(), valueString.length(), parsedLength); + numericValue = charactersToFloat(value.characters16(), value.length(), parsedLength); if (!parsedLength) { - reportViewportWarning(document, UnrecognizedViewportArgumentValueError, valueString, keyString); + reportViewportWarning(document, UnrecognizedViewportArgumentValueError, value, key); if (ok) *ok = false; return 0; } - if (parsedLength < valueString.length()) - reportViewportWarning(document, TruncatedViewportArgumentValueError, valueString, keyString); + if (parsedLength < value.length()) + reportViewportWarning(document, TruncatedViewportArgumentValueError, value, key); if (ok) *ok = true; - return value; + return numericValue; } -static float findSizeValue(const String& keyString, const String& valueString, Document* document) +static float findSizeValue(Document& document, StringView key, StringView value, bool* valueWasExplicit = nullptr) { // 1) Non-negative number values are translated to px lengths. // 2) Negative number values are translated to auto. // 3) device-width and device-height are used as keywords. // 4) Other keywords and unknown values translate to 0.0. - if (equalIgnoringCase(valueString, "device-width")) + if (valueWasExplicit) + *valueWasExplicit = true; + + if (equalLettersIgnoringASCIICase(value, "device-width")) return ViewportArguments::ValueDeviceWidth; - if (equalIgnoringCase(valueString, "device-height")) + + if (equalLettersIgnoringASCIICase(value, "device-height")) return ViewportArguments::ValueDeviceHeight; - float value = numericPrefix(keyString, valueString, document); + float sizeValue = numericPrefix(document, key, value); - if (value < 0) + if (sizeValue < 0) { + if (valueWasExplicit) + *valueWasExplicit = false; return ViewportArguments::ValueAuto; + } - return value; + return sizeValue; } -static float findScaleValue(const String& keyString, const String& valueString, Document* document) +static float findScaleValue(Document& document, StringView key, StringView value) { // 1) Non-negative number values are translated to <number> values. // 2) Negative number values are translated to auto. @@ -348,89 +343,69 @@ static float findScaleValue(const String& keyString, const String& valueString, // 4) device-width and device-height are translated to 10.0. // 5) no and unknown values are translated to 0.0 - if (equalIgnoringCase(valueString, "yes")) + if (equalLettersIgnoringASCIICase(value, "yes")) return 1; - if (equalIgnoringCase(valueString, "no")) + if (equalLettersIgnoringASCIICase(value, "no")) return 0; - if (equalIgnoringCase(valueString, "device-width")) + if (equalLettersIgnoringASCIICase(value, "device-width")) return 10; - if (equalIgnoringCase(valueString, "device-height")) + if (equalLettersIgnoringASCIICase(value, "device-height")) return 10; - float value = numericPrefix(keyString, valueString, document); + float numericValue = numericPrefix(document, key, value); - if (value < 0) + if (numericValue < 0) return ViewportArguments::ValueAuto; - if (value > 10.0) - reportViewportWarning(document, MaximumScaleTooLargeError, String(), String()); + if (numericValue > 10.0) + reportViewportWarning(document, MaximumScaleTooLargeError); - return value; + return numericValue; } -static float findUserScalableValue(const String& keyString, const String& valueString, Document* document) +// FIXME: It's kind of bizarre to use floating point values of 1 and 0 to represent true and false. +static float findBooleanValue(Document& document, StringView key, StringView value) { // yes and no are used as keywords. // Numbers >= 1, numbers <= -1, device-width and device-height are mapped to yes. // Numbers in the range <-1, 1>, and unknown values, are mapped to no. - if (equalIgnoringCase(valueString, "yes")) + if (equalLettersIgnoringASCIICase(value, "yes")) return 1; - if (equalIgnoringCase(valueString, "no")) + if (equalLettersIgnoringASCIICase(value, "no")) return 0; - if (equalIgnoringCase(valueString, "device-width")) + if (equalLettersIgnoringASCIICase(value, "device-width")) return 1; - if (equalIgnoringCase(valueString, "device-height")) + if (equalLettersIgnoringASCIICase(value, "device-height")) return 1; - - float value = numericPrefix(keyString, valueString, document); - - if (fabs(value) < 1) - return 0; - - return 1; + return std::abs(numericPrefix(document, key, value)) >= 1 ? 1 : 0; } -void setViewportFeature(const String& keyString, const String& valueString, Document* document, void* data) +void setViewportFeature(ViewportArguments& arguments, Document& document, StringView key, StringView value) { - ViewportArguments* arguments = static_cast<ViewportArguments*>(data); - - if (keyString == "width") - arguments->width = findSizeValue(keyString, valueString, document); - else if (keyString == "height") - arguments->height = findSizeValue(keyString, valueString, document); - else if (keyString == "initial-scale") - arguments->zoom = findScaleValue(keyString, valueString, document); - else if (keyString == "minimum-scale") - arguments->minZoom = findScaleValue(keyString, valueString, document); - else if (keyString == "maximum-scale") - arguments->maxZoom = findScaleValue(keyString, valueString, document); - else if (keyString == "user-scalable") - arguments->userZoom = findUserScalableValue(keyString, valueString, document); + if (equalLettersIgnoringASCIICase(key, "width")) + arguments.width = findSizeValue(document, key, value, &arguments.widthWasExplicit); + else if (equalLettersIgnoringASCIICase(key, "height")) + arguments.height = findSizeValue(document, key, value); + else if (equalLettersIgnoringASCIICase(key, "initial-scale")) + arguments.zoom = findScaleValue(document, key, value); + else if (equalLettersIgnoringASCIICase(key, "minimum-scale")) + arguments.minZoom = findScaleValue(document, key, value); + else if (equalLettersIgnoringASCIICase(key, "maximum-scale")) + arguments.maxZoom = findScaleValue(document, key, value); + else if (equalLettersIgnoringASCIICase(key, "user-scalable")) + arguments.userZoom = findBooleanValue(document, key, value); #if PLATFORM(IOS) - else if (keyString == "minimal-ui") - arguments->minimalUI = true; + else if (equalLettersIgnoringASCIICase(key, "minimal-ui")) { + // FIXME: Ignore silently for now. This code should eventually be removed + // so we start giving the warning in the web inspector as for other unimplemented keys. + } #endif + else if (equalLettersIgnoringASCIICase(key, "shrink-to-fit")) + arguments.shrinkToFit = findBooleanValue(document, key, value); else - reportViewportWarning(document, UnrecognizedViewportArgumentKeyError, keyString, String()); -} - -#if PLATFORM(IOS) -void finalizeViewportArguments(ViewportArguments& arguments) -{ - CGSize screenSize = wkGetViewportScreenSize(); - - if (arguments.width == ViewportArguments::ValueDeviceWidth) - arguments.width = screenSize.width; - else if (arguments.width == ViewportArguments::ValueDeviceHeight) - arguments.width = screenSize.height; - - if (arguments.height == ViewportArguments::ValueDeviceWidth) - arguments.height = screenSize.width; - else if (arguments.height == ViewportArguments::ValueDeviceHeight) - arguments.height = screenSize.height; + reportViewportWarning(document, UnrecognizedViewportArgumentKeyError, key); } -#endif static const char* viewportErrorMessageTemplate(ViewportErrorCode errorCode) { @@ -448,34 +423,55 @@ static MessageLevel viewportErrorMessageLevel(ViewportErrorCode errorCode) { switch (errorCode) { case TruncatedViewportArgumentValueError: - return WarningMessageLevel; + return MessageLevel::Warning; case UnrecognizedViewportArgumentKeyError: case UnrecognizedViewportArgumentValueError: case MaximumScaleTooLargeError: - return ErrorMessageLevel; + return MessageLevel::Error; } ASSERT_NOT_REACHED(); - return ErrorMessageLevel; + return MessageLevel::Error; } -void reportViewportWarning(Document* document, ViewportErrorCode errorCode, const String& replacement1, const String& replacement2) +void reportViewportWarning(Document& document, ViewportErrorCode errorCode, StringView replacement1, StringView replacement2) { - Frame* frame = document->frame(); - if (!frame) + // FIXME: Why is this null check needed? Can't addConsoleMessage deal with this? + if (!document.frame()) return; String message = viewportErrorMessageTemplate(errorCode); if (!replacement1.isNull()) - message.replace("%replacement1", replacement1); + message.replace("%replacement1", replacement1.toStringWithoutCopying()); + // FIXME: This will do the wrong thing if replacement1 contains the substring "%replacement2". if (!replacement2.isNull()) - message.replace("%replacement2", replacement2); + message.replace("%replacement2", replacement2.toStringWithoutCopying()); - if ((errorCode == UnrecognizedViewportArgumentValueError || errorCode == TruncatedViewportArgumentValueError) && replacement1.find(';') != WTF::notFound) + if ((errorCode == UnrecognizedViewportArgumentValueError || errorCode == TruncatedViewportArgumentValueError) && replacement1.contains(';')) message.append(" Note that ';' is not a separator in viewport values. The list should be comma-separated."); // FIXME: This message should be moved off the console once a solution to https://bugs.webkit.org/show_bug.cgi?id=103274 exists. - document->addConsoleMessage(RenderingMessageSource, viewportErrorMessageLevel(errorCode), message); + document.addConsoleMessage(MessageSource::Rendering, viewportErrorMessageLevel(errorCode), message); +} + +TextStream& operator<<(TextStream& ts, const ViewportArguments& viewportArguments) +{ + ts.increaseIndent(); + + ts << "\n"; + ts.writeIndent(); + ts << "(width " << viewportArguments.width << ", minWidth " << viewportArguments.minWidth << ", maxWidth " << viewportArguments.maxWidth << ")"; + + ts << "\n"; + ts.writeIndent(); + ts << "(height " << viewportArguments.height << ", minHeight " << viewportArguments.minHeight << ", maxHeight " << viewportArguments.maxHeight << ")"; + + ts << "\n"; + ts.writeIndent(); + ts << "(zoom " << viewportArguments.zoom << ", minZoom " << viewportArguments.minZoom << ", maxZoom " << viewportArguments.maxZoom << ")"; + ts.decreaseIndent(); + + return ts; } } // namespace WebCore diff --git a/Source/WebCore/dom/ViewportArguments.h b/Source/WebCore/dom/ViewportArguments.h index e45e3cfd3..4e6907f58 100644 --- a/Source/WebCore/dom/ViewportArguments.h +++ b/Source/WebCore/dom/ViewportArguments.h @@ -25,8 +25,7 @@ * */ -#ifndef ViewportArguments_h -#define ViewportArguments_h +#pragma once #include "FloatSize.h" #include <wtf/Forward.h> @@ -51,6 +50,7 @@ struct ViewportAttributes { float userScalable; float orientation; + float shrinkToFit; }; struct ViewportArguments { @@ -58,11 +58,6 @@ struct ViewportArguments { enum Type { // These are ordered in increasing importance. Implicit, -#if ENABLE(LEGACY_VIEWPORT_ADAPTION) - XHTMLMobileProfile, - HandheldFriendlyMeta, - MobileOptimizedMeta, -#endif #if PLATFORM(IOS) PluginDocument, ImageDocument, @@ -81,49 +76,30 @@ struct ViewportArguments { explicit ViewportArguments(Type type = Implicit) : type(type) - , width(ValueAuto) - , minWidth(ValueAuto) - , maxWidth(ValueAuto) - , height(ValueAuto) - , minHeight(ValueAuto) - , maxHeight(ValueAuto) - , zoom(ValueAuto) - , minZoom(ValueAuto) - , maxZoom(ValueAuto) - , userZoom(ValueAuto) - , orientation(ValueAuto) -#if PLATFORM(IOS) - , minimalUI(false) -#endif { } // All arguments are in CSS units. ViewportAttributes resolve(const FloatSize& initialViewportSize, const FloatSize& deviceSize, int defaultWidth) const; - float width; - float minWidth; - float maxWidth; - float height; - float minHeight; - float maxHeight; - float zoom; - float minZoom; - float maxZoom; - float userZoom; - float orientation; -#if PLATFORM(IOS) - bool minimalUI; -#endif + float width { ValueAuto }; + float minWidth { ValueAuto }; + float maxWidth { ValueAuto }; + float height { ValueAuto }; + float minHeight { ValueAuto }; + float maxHeight { ValueAuto }; + float zoom { ValueAuto }; + float minZoom { ValueAuto }; + float maxZoom { ValueAuto }; + float userZoom { ValueAuto }; + float orientation { ValueAuto }; + float shrinkToFit { ValueAuto }; + bool widthWasExplicit { false }; bool operator==(const ViewportArguments& other) const { // Used for figuring out whether to reset the viewport or not, // thus we are not taking type into account. -#if PLATFORM(IOS) - // We ignore minimalUI for the same reason -- it is a higher-level - // property that doesn't affect the actual viewport. -#endif return width == other.width && minWidth == other.minWidth && maxWidth == other.maxWidth @@ -134,7 +110,9 @@ struct ViewportArguments { && minZoom == other.minZoom && maxZoom == other.maxZoom && userZoom == other.userZoom - && orientation == other.orientation; + && orientation == other.orientation + && shrinkToFit == other.shrinkToFit + && widthWasExplicit == other.widthWasExplicit; } bool operator!=(const ViewportArguments& other) const @@ -149,19 +127,14 @@ struct ViewportArguments { #endif }; -ViewportAttributes computeViewportAttributes(ViewportArguments args, int desktopWidth, int deviceWidth, int deviceHeight, float devicePixelRatio, IntSize visibleViewport); +WEBCORE_EXPORT ViewportAttributes computeViewportAttributes(ViewportArguments args, int desktopWidth, int deviceWidth, int deviceHeight, float devicePixelRatio, IntSize visibleViewport); -void restrictMinimumScaleFactorToViewportSize(ViewportAttributes& result, IntSize visibleViewport, float devicePixelRatio); -void restrictScaleFactorToInitialScaleIfNotUserScalable(ViewportAttributes& result); +WEBCORE_EXPORT void restrictMinimumScaleFactorToViewportSize(ViewportAttributes& result, IntSize visibleViewport, float devicePixelRatio); +WEBCORE_EXPORT void restrictScaleFactorToInitialScaleIfNotUserScalable(ViewportAttributes& result); float computeMinimumScaleFactorForContentContained(const ViewportAttributes& result, const IntSize& viewportSize, const IntSize& contentSize); -void setViewportFeature(const String& keyString, const String& valueString, Document*, void* data); -void reportViewportWarning(Document*, ViewportErrorCode, const String& replacement1, const String& replacement2); +void setViewportFeature(ViewportArguments&, Document&, StringView key, StringView value); -#if PLATFORM(IOS) -void finalizeViewportArguments(ViewportArguments&); -#endif +TextStream& operator<<(TextStream&, const ViewportArguments&); } // namespace WebCore - -#endif // ViewportArguments_h diff --git a/Source/WebCore/dom/VisitedLinkState.cpp b/Source/WebCore/dom/VisitedLinkState.cpp index 3b12f6c80..5920bdadc 100644 --- a/Source/WebCore/dom/VisitedLinkState.cpp +++ b/Source/WebCore/dom/VisitedLinkState.cpp @@ -33,31 +33,24 @@ #include "Frame.h" #include "HTMLAnchorElement.h" #include "Page.h" -#include "PageGroup.h" -#include "PlatformStrategies.h" -#include "VisitedLinkStrategy.h" +#include "VisitedLinkStore.h" #include "XLinkNames.h" namespace WebCore { using namespace HTMLNames; -inline static const AtomicString* linkAttribute(Element& element) +inline static const AtomicString* linkAttribute(const Element& element) { if (!element.isLink()) return 0; if (element.isHTMLElement()) - return &element.fastGetAttribute(HTMLNames::hrefAttr); + return &element.attributeWithoutSynchronization(HTMLNames::hrefAttr); if (element.isSVGElement()) return &element.getAttribute(XLinkNames::hrefAttr); return 0; } -PassOwnPtr<VisitedLinkState> VisitedLinkState::create(Document& document) -{ - return adoptPtr(new VisitedLinkState(document)); -} - VisitedLinkState::VisitedLinkState(Document& document) : m_document(document) { @@ -69,14 +62,14 @@ void VisitedLinkState::invalidateStyleForAllLinks() return; for (auto& element : descendantsOfType<Element>(m_document)) { if (element.isLink()) - element.setNeedsStyleRecalc(); + element.invalidateStyleForSubtree(); } } -inline static LinkHash linkHashForElement(Document& document, Element& element) +inline static LinkHash linkHashForElement(Document& document, const Element& element) { - if (isHTMLAnchorElement(element)) - return toHTMLAnchorElement(element).visitedLinkHash(); + if (is<HTMLAnchorElement>(element)) + return downcast<HTMLAnchorElement>(element).visitedLinkHash(); if (const AtomicString* attribute = linkAttribute(element)) return WebCore::visitedLinkHash(document.baseURL(), *attribute); return 0; @@ -88,11 +81,11 @@ void VisitedLinkState::invalidateStyleForLink(LinkHash linkHash) return; for (auto& element : descendantsOfType<Element>(m_document)) { if (linkHashForElement(m_document, element) == linkHash) - element.setNeedsStyleRecalc(); + element.invalidateStyleForSubtree(); } } -EInsideLink VisitedLinkState::determineLinkStateSlowCase(Element& element) +EInsideLink VisitedLinkState::determineLinkStateSlowCase(const Element& element) { ASSERT(element.isLink()); @@ -106,8 +99,8 @@ EInsideLink VisitedLinkState::determineLinkStateSlowCase(Element& element) return InsideVisitedLink; LinkHash hash; - if (isHTMLAnchorElement(element)) - hash = toHTMLAnchorElement(element).visitedLinkHash(); + if (is<HTMLAnchorElement>(element)) + hash = downcast<HTMLAnchorElement>(element).visitedLinkHash(); else hash = WebCore::visitedLinkHash(element.document().baseURL(), *attribute); @@ -124,7 +117,10 @@ EInsideLink VisitedLinkState::determineLinkStateSlowCase(Element& element) m_linksCheckedForVisitedState.add(hash); - return platformStrategies()->visitedLinkStrategy()->isLinkVisited(page, hash, element.document().baseURL(), *attribute) ? InsideVisitedLink : InsideUnvisitedLink; -} + if (!page->visitedLinkStore().isLinkVisited(*page, hash, element.document().baseURL(), *attribute)) + return InsideUnvisitedLink; + return InsideVisitedLink; } + +} // namespace WebCore diff --git a/Source/WebCore/dom/VisitedLinkState.h b/Source/WebCore/dom/VisitedLinkState.h index 60273bb10..2a142ad86 100644 --- a/Source/WebCore/dom/VisitedLinkState.h +++ b/Source/WebCore/dom/VisitedLinkState.h @@ -26,14 +26,12 @@ * Boston, MA 02110-1301, USA. */ -#ifndef VisitedLinkState_h -#define VisitedLinkState_h +#pragma once #include "Element.h" #include "LinkHash.h" #include "RenderStyleConstants.h" #include <wtf/HashSet.h> -#include <wtf/OwnPtr.h> namespace WebCore { @@ -42,29 +40,24 @@ class Document; class VisitedLinkState { WTF_MAKE_FAST_ALLOCATED; public: - static PassOwnPtr<VisitedLinkState> create(Document&); + explicit VisitedLinkState(Document&); void invalidateStyleForAllLinks(); void invalidateStyleForLink(LinkHash); - EInsideLink determineLinkState(Element*); + EInsideLink determineLinkState(const Element&); private: - explicit VisitedLinkState(Document&); - - EInsideLink determineLinkStateSlowCase(Element&); + EInsideLink determineLinkStateSlowCase(const Element&); Document& m_document; HashSet<LinkHash, LinkHashHash> m_linksCheckedForVisitedState; }; -inline EInsideLink VisitedLinkState::determineLinkState(Element* element) +inline EInsideLink VisitedLinkState::determineLinkState(const Element& element) { - if (!element || !element->isLink()) + if (!element.isLink()) return NotInsideLink; - return determineLinkStateSlowCase(*element); + return determineLinkStateSlowCase(element); } -} - -#endif - +} // namespace WebCore diff --git a/Source/WebCore/dom/WebKitAnimationEvent.cpp b/Source/WebCore/dom/WebKitAnimationEvent.cpp index a2d21a343..bf116f46c 100644 --- a/Source/WebCore/dom/WebKitAnimationEvent.cpp +++ b/Source/WebCore/dom/WebKitAnimationEvent.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,23 +26,10 @@ #include "config.h" #include "WebKitAnimationEvent.h" -#include "EventNames.h" - namespace WebCore { -WebKitAnimationEventInit::WebKitAnimationEventInit() - : animationName() - , elapsedTime(0.0) -{ -} - -WebKitAnimationEvent::WebKitAnimationEvent() - : m_elapsedTime(0.0) -{ -} - -WebKitAnimationEvent::WebKitAnimationEvent(const AtomicString& type, const WebKitAnimationEventInit& initializer) - : Event(type, initializer) +WebKitAnimationEvent::WebKitAnimationEvent(const AtomicString& type, const Init& initializer, IsTrusted isTrusted) + : Event(type, initializer, isTrusted) , m_animationName(initializer.animationName) , m_elapsedTime(initializer.elapsedTime) { diff --git a/Source/WebCore/dom/WebKitAnimationEvent.h b/Source/WebCore/dom/WebKitAnimationEvent.h index 9f3c5b3fb..5506b6197 100644 --- a/Source/WebCore/dom/WebKitAnimationEvent.h +++ b/Source/WebCore/dom/WebKitAnimationEvent.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -23,33 +23,27 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef WebKitAnimationEvent_h -#define WebKitAnimationEvent_h +#pragma once #include "Event.h" namespace WebCore { -struct WebKitAnimationEventInit : public EventInit { - WebKitAnimationEventInit(); - - String animationName; - double elapsedTime; -}; - -class WebKitAnimationEvent : public Event { +class WebKitAnimationEvent final : public Event { public: - static PassRefPtr<WebKitAnimationEvent> create() - { - return adoptRef(new WebKitAnimationEvent); - } - static PassRefPtr<WebKitAnimationEvent> create(const AtomicString& type, const String& animationName, double elapsedTime) + static Ref<WebKitAnimationEvent> create(const AtomicString& type, const String& animationName, double elapsedTime) { - return adoptRef(new WebKitAnimationEvent(type, animationName, elapsedTime)); + return adoptRef(*new WebKitAnimationEvent(type, animationName, elapsedTime)); } - static PassRefPtr<WebKitAnimationEvent> create(const AtomicString& type, const WebKitAnimationEventInit& initializer) + + struct Init : EventInit { + String animationName; + double elapsedTime { 0.0 }; + }; + + static Ref<WebKitAnimationEvent> create(const AtomicString& type, const Init& initializer, IsTrusted isTrusted = IsTrusted::No) { - return adoptRef(new WebKitAnimationEvent(type, initializer)); + return adoptRef(*new WebKitAnimationEvent(type, initializer, isTrusted)); } virtual ~WebKitAnimationEvent(); @@ -57,17 +51,14 @@ public: const String& animationName() const; double elapsedTime() const; - virtual EventInterface eventInterface() const override; + EventInterface eventInterface() const override; private: - WebKitAnimationEvent(); WebKitAnimationEvent(const AtomicString& type, const String& animationName, double elapsedTime); - WebKitAnimationEvent(const AtomicString&, const WebKitAnimationEventInit&); + WebKitAnimationEvent(const AtomicString&, const Init&, IsTrusted); String m_animationName; double m_elapsedTime; }; } // namespace WebCore - -#endif // WebKitAnimationEvent_h diff --git a/Source/WebCore/dom/WebKitAnimationEvent.idl b/Source/WebCore/dom/WebKitAnimationEvent.idl index 6f1844b16..7c1bbec58 100644 --- a/Source/WebCore/dom/WebKitAnimationEvent.idl +++ b/Source/WebCore/dom/WebKitAnimationEvent.idl @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,9 +24,13 @@ */ [ - ConstructorTemplate=Event + Constructor(DOMString type, optional WebKitAnimationEventInit eventInitDict), ] interface WebKitAnimationEvent : Event { - [InitializedByEventConstructor] readonly attribute DOMString animationName; - [InitializedByEventConstructor] readonly attribute double elapsedTime; + readonly attribute DOMString animationName; + readonly attribute double elapsedTime; }; +dictionary WebKitAnimationEventInit : EventInit { + DOMString animationName = ""; + double elapsedTime = 0; +}; diff --git a/Source/WebCore/dom/WebKitNamedFlow.cpp b/Source/WebCore/dom/WebKitNamedFlow.cpp index cff2dd659..f4cc9f224 100644 --- a/Source/WebCore/dom/WebKitNamedFlow.cpp +++ b/Source/WebCore/dom/WebKitNamedFlow.cpp @@ -30,7 +30,9 @@ #include "config.h" #include "WebKitNamedFlow.h" +#include "EventNames.h" #include "NamedFlowCollection.h" +#include "NoEventDispatchAssertion.h" #include "RenderNamedFlowFragment.h" #include "RenderNamedFlowThread.h" #include "RenderRegion.h" @@ -39,10 +41,10 @@ namespace WebCore { -WebKitNamedFlow::WebKitNamedFlow(PassRefPtr<NamedFlowCollection> manager, const AtomicString& flowThreadName) +WebKitNamedFlow::WebKitNamedFlow(NamedFlowCollection& manager, const AtomicString& flowThreadName) : m_flowThreadName(flowThreadName) - , m_flowManager(manager) - , m_parentFlowThread(0) + , m_flowManager(&manager) + , m_parentFlowThread(nullptr) { } @@ -52,9 +54,9 @@ WebKitNamedFlow::~WebKitNamedFlow() m_flowManager->discardNamedFlow(this); } -PassRefPtr<WebKitNamedFlow> WebKitNamedFlow::create(PassRefPtr<NamedFlowCollection> manager, const AtomicString& flowThreadName) +Ref<WebKitNamedFlow> WebKitNamedFlow::create(NamedFlowCollection& manager, const AtomicString& flowThreadName) { - return adoptRef(new WebKitNamedFlow(manager, flowThreadName)); + return adoptRef(*new WebKitNamedFlow(manager, flowThreadName)); } const AtomicString& WebKitNamedFlow::name() const @@ -69,7 +71,11 @@ bool WebKitNamedFlow::overset() const // The renderer may be destroyed or created after the style update. // Because this is called from JS, where the wrapper keeps a reference to the NamedFlow, no guard is necessary. - return m_parentFlowThread ? m_parentFlowThread->overset() : true; + if (!m_parentFlowThread || !m_parentFlowThread->hasRegions()) + return true; + + const auto& namedFlowFragment = downcast<RenderNamedFlowFragment>(*m_parentFlowThread->lastRegion()); + return namedFlowFragment.regionOversetState() == RegionOverset; } static inline bool inFlowThread(RenderObject* renderer, RenderNamedFlowThread* flowThread) @@ -100,23 +106,23 @@ int WebKitNamedFlow::firstEmptyRegionIndex() const int countNonPseudoRegions = -1; for (const auto& renderRegion : regionList) { - const RenderNamedFlowFragment* namedFlowFragment = toRenderNamedFlowFragment(renderRegion); + const auto& namedFlowFragment = downcast<RenderNamedFlowFragment>(*renderRegion); // FIXME: Pseudo-elements are not included in the list. // They will be included when we will properly support the Region interface // http://dev.w3.org/csswg/css-regions/#the-region-interface - if (namedFlowFragment->isPseudoElementRegion()) + if (namedFlowFragment.isPseudoElementRegion()) continue; - countNonPseudoRegions++; - if (namedFlowFragment->regionOversetState() == RegionEmpty) + ++countNonPseudoRegions; + if (namedFlowFragment.regionOversetState() == RegionEmpty) return countNonPseudoRegions; } return -1; } -PassRefPtr<NodeList> WebKitNamedFlow::getRegionsByContent(Node* contentNode) +Ref<NodeList> WebKitNamedFlow::getRegionsByContent(Node* contentNode) { if (!contentNode) - return StaticElementList::createEmpty(); + return StaticElementList::create(); if (m_flowManager->document()) m_flowManager->document()->updateLayoutIgnorePendingStylesheets(); @@ -124,30 +130,30 @@ PassRefPtr<NodeList> WebKitNamedFlow::getRegionsByContent(Node* contentNode) // The renderer may be destroyed or created after the style update. // Because this is called from JS, where the wrapper keeps a reference to the NamedFlow, no guard is necessary. if (!m_parentFlowThread) - return StaticElementList::createEmpty(); + return StaticElementList::create(); Vector<Ref<Element>> regionElements; if (inFlowThread(contentNode->renderer(), m_parentFlowThread)) { const RenderRegionList& regionList = m_parentFlowThread->renderRegionList(); for (const auto& renderRegion : regionList) { - const RenderNamedFlowFragment* namedFlowFragment = toRenderNamedFlowFragment(renderRegion); + const auto& namedFlowFragment = downcast<RenderNamedFlowFragment>(*renderRegion); // FIXME: Pseudo-elements are not included in the list. // They will be included when we will properly support the Region interface // http://dev.w3.org/csswg/css-regions/#the-region-interface - if (namedFlowFragment->isPseudoElementRegion()) + if (namedFlowFragment.isPseudoElementRegion()) continue; - if (m_parentFlowThread->objectInFlowRegion(contentNode->renderer(), namedFlowFragment)) { - ASSERT(namedFlowFragment->generatingElement()); - regionElements.append(*namedFlowFragment->generatingElement()); + if (m_parentFlowThread->objectInFlowRegion(contentNode->renderer(), &namedFlowFragment)) { + ASSERT(namedFlowFragment.generatingElement()); + regionElements.append(*namedFlowFragment.generatingElement()); } } } - return StaticElementList::adopt(regionElements); + return StaticElementList::create(WTFMove(regionElements)); } -PassRefPtr<NodeList> WebKitNamedFlow::getRegions() +Ref<NodeList> WebKitNamedFlow::getRegions() { if (m_flowManager->document()) m_flowManager->document()->updateLayoutIgnorePendingStylesheets(); @@ -155,26 +161,26 @@ PassRefPtr<NodeList> WebKitNamedFlow::getRegions() // The renderer may be destroyed or created after the style update. // Because this is called from JS, where the wrapper keeps a reference to the NamedFlow, no guard is necessary. if (!m_parentFlowThread) - return StaticElementList::createEmpty(); + return StaticElementList::create(); Vector<Ref<Element>> regionElements; const RenderRegionList& regionList = m_parentFlowThread->renderRegionList(); for (const auto& renderRegion : regionList) { - const RenderNamedFlowFragment* namedFlowFragment = toRenderNamedFlowFragment(renderRegion); + const auto& namedFlowFragment = downcast<RenderNamedFlowFragment>(*renderRegion); // FIXME: Pseudo-elements are not included in the list. // They will be included when we will properly support the Region interface // http://dev.w3.org/csswg/css-regions/#the-region-interface - if (namedFlowFragment->isPseudoElementRegion()) + if (namedFlowFragment.isPseudoElementRegion()) continue; - ASSERT(namedFlowFragment->generatingElement()); - regionElements.append(*namedFlowFragment->generatingElement()); + ASSERT(namedFlowFragment.generatingElement()); + regionElements.append(*namedFlowFragment.generatingElement()); } - return StaticElementList::adopt(regionElements); + return StaticElementList::create(WTFMove(regionElements)); } -PassRefPtr<NodeList> WebKitNamedFlow::getContent() +Ref<NodeList> WebKitNamedFlow::getContent() { if (m_flowManager->document()) m_flowManager->document()->updateLayoutIgnorePendingStylesheets(); @@ -182,17 +188,17 @@ PassRefPtr<NodeList> WebKitNamedFlow::getContent() // The renderer may be destroyed or created after the style update. // Because this is called from JS, where the wrapper keeps a reference to the NamedFlow, no guard is necessary. if (!m_parentFlowThread) - return StaticElementList::createEmpty(); + return StaticElementList::create(); + auto& contentElementsList = m_parentFlowThread->contentElements(); Vector<Ref<Element>> contentElements; - - const NamedFlowContentElements& contentElementsList = m_parentFlowThread->contentElements(); + contentElements.reserveInitialCapacity(contentElementsList.size()); for (auto& element : contentElementsList) { ASSERT(element->computedStyle()->flowThread() == m_parentFlowThread->flowThreadName()); - contentElements.append(*element); + contentElements.uncheckedAppend(*element); } - return StaticElementList::adopt(contentElements); + return StaticElementList::create(WTFMove(contentElements)); } void WebKitNamedFlow::setRenderer(RenderNamedFlowThread* parentFlowThread) @@ -204,20 +210,9 @@ void WebKitNamedFlow::setRenderer(RenderNamedFlowThread* parentFlowThread) m_parentFlowThread = parentFlowThread; } -void WebKitNamedFlow::dispatchRegionLayoutUpdateEvent() -{ - ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden()); - - // If the flow is in the "NULL" state the event should not be dispatched any more. - if (flowState() == FlowStateNull) - return; - - dispatchEvent(UIEvent::create(eventNames().webkitregionlayoutupdateEvent, false, false, m_flowManager->document()->defaultView(), 0)); -} - void WebKitNamedFlow::dispatchRegionOversetChangeEvent() { - ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden()); + ASSERT_WITH_SECURITY_IMPLICATION(NoEventDispatchAssertion::isEventAllowedInMainThread()); // If the flow is in the "NULL" state the event should not be dispatched any more. if (flowState() == FlowStateNull) diff --git a/Source/WebCore/dom/WebKitNamedFlow.h b/Source/WebCore/dom/WebKitNamedFlow.h index 6b1625e74..b63bac15a 100644 --- a/Source/WebCore/dom/WebKitNamedFlow.h +++ b/Source/WebCore/dom/WebKitNamedFlow.h @@ -27,19 +27,15 @@ * SUCH DAMAGE. */ -#ifndef WebKitNamedFlow_h -#define WebKitNamedFlow_h +#pragma once #include "EventTarget.h" - -#include <wtf/ListHashSet.h> #include <wtf/RefCounted.h> #include <wtf/RefPtr.h> #include <wtf/text/AtomicString.h> namespace WebCore { -class Document; class NamedFlowCollection; class Node; class NodeList; @@ -48,22 +44,22 @@ class ScriptExecutionContext; class WebKitNamedFlow final : public RefCounted<WebKitNamedFlow>, public EventTargetWithInlineData { public: - static PassRefPtr<WebKitNamedFlow> create(PassRefPtr<NamedFlowCollection> manager, const AtomicString& flowThreadName); + static Ref<WebKitNamedFlow> create(NamedFlowCollection& manager, const AtomicString& flowThreadName); ~WebKitNamedFlow(); const AtomicString& name() const; bool overset() const; int firstEmptyRegionIndex() const; - PassRefPtr<NodeList> getRegionsByContent(Node*); - PassRefPtr<NodeList> getRegions(); - PassRefPtr<NodeList> getContent(); + Ref<NodeList> getRegionsByContent(Node*); + Ref<NodeList> getRegions(); + Ref<NodeList> getContent(); using RefCounted<WebKitNamedFlow>::ref; using RefCounted<WebKitNamedFlow>::deref; - virtual EventTargetInterface eventTargetInterface() const override { return WebKitNamedFlowEventTargetInterfaceType; } - virtual ScriptExecutionContext* scriptExecutionContext() const override; + EventTargetInterface eventTargetInterface() const override { return WebKitNamedFlowEventTargetInterfaceType; } + ScriptExecutionContext* scriptExecutionContext() const override; // This function is called from the JS binding code to determine if the NamedFlow object is reachable or not. // If the object has listeners, the object should only be discarded if the parent Document is not reachable. @@ -78,15 +74,14 @@ public: FlowState flowState() const { return m_parentFlowThread ? FlowStateCreated : FlowStateNull; } - void dispatchRegionLayoutUpdateEvent(); void dispatchRegionOversetChangeEvent(); private: - WebKitNamedFlow(PassRefPtr<NamedFlowCollection>, const AtomicString&); + WebKitNamedFlow(NamedFlowCollection&, const AtomicString&); // EventTarget implementation. - virtual void refEventTarget() override { ref(); } - virtual void derefEventTarget() override { deref(); } + void refEventTarget() override { ref(); } + void derefEventTarget() override { deref(); } // The name of the flow thread as specified in CSS. AtomicString m_flowThreadName; @@ -95,6 +90,4 @@ private: RenderNamedFlowThread* m_parentFlowThread; }; -} - -#endif +} // namespace WebCore diff --git a/Source/WebCore/dom/WebKitNamedFlow.idl b/Source/WebCore/dom/WebKitNamedFlow.idl index b27e0d05a..8a3f71a06 100644 --- a/Source/WebCore/dom/WebKitNamedFlow.idl +++ b/Source/WebCore/dom/WebKitNamedFlow.idl @@ -28,24 +28,15 @@ */ [ - NoInterfaceObject, - EventTarget, - JSGenerateToJSObject, GenerateIsReachable=ImplOwnerNodeRoot, -] interface WebKitNamedFlow { + NoInterfaceObject, +] interface WebKitNamedFlow : EventTarget +{ readonly attribute DOMString name; readonly attribute boolean overset; readonly attribute long firstEmptyRegionIndex; - NodeList getRegionsByContent(Node contentNode); + // FIXME: contentNode should not be nullable. + NodeList getRegionsByContent(Node? contentNode); NodeList getRegions(); NodeList getContent(); - - // EventTarget interface - void addEventListener(DOMString type, - EventListener listener, - optional boolean useCapture); - void removeEventListener(DOMString type, - EventListener listener, - optional boolean useCapture); - [RaisesException] boolean dispatchEvent(Event event); }; diff --git a/Source/WebCore/dom/WebKitTransitionEvent.cpp b/Source/WebCore/dom/WebKitTransitionEvent.cpp index 78dcdbcf2..d45182f7c 100644 --- a/Source/WebCore/dom/WebKitTransitionEvent.cpp +++ b/Source/WebCore/dom/WebKitTransitionEvent.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,20 +26,8 @@ #include "config.h" #include "WebKitTransitionEvent.h" -#include "EventNames.h" - namespace WebCore { -WebKitTransitionEventInit::WebKitTransitionEventInit() - : elapsedTime(0) -{ -} - -WebKitTransitionEvent::WebKitTransitionEvent() - : m_elapsedTime(0) -{ -} - WebKitTransitionEvent::WebKitTransitionEvent(const AtomicString& type, const String& propertyName, double elapsedTime, const String& pseudoElement) : Event(type, true, true) , m_propertyName(propertyName) @@ -48,8 +36,8 @@ WebKitTransitionEvent::WebKitTransitionEvent(const AtomicString& type, const Str { } -WebKitTransitionEvent::WebKitTransitionEvent(const AtomicString& type, const WebKitTransitionEventInit& initializer) - : Event(type, initializer) +WebKitTransitionEvent::WebKitTransitionEvent(const AtomicString& type, const Init& initializer, IsTrusted isTrusted) + : Event(type, initializer, isTrusted) , m_propertyName(initializer.propertyName) , m_elapsedTime(initializer.elapsedTime) , m_pseudoElement(initializer.pseudoElement) diff --git a/Source/WebCore/dom/WebKitTransitionEvent.h b/Source/WebCore/dom/WebKitTransitionEvent.h index 4f6573b73..76643ff62 100644 --- a/Source/WebCore/dom/WebKitTransitionEvent.h +++ b/Source/WebCore/dom/WebKitTransitionEvent.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -23,34 +23,28 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef WebKitTransitionEvent_h -#define WebKitTransitionEvent_h +#pragma once #include "Event.h" namespace WebCore { -struct WebKitTransitionEventInit : public EventInit { - WebKitTransitionEventInit(); - - String propertyName; - double elapsedTime; - String pseudoElement; -}; - -class WebKitTransitionEvent : public Event { +class WebKitTransitionEvent final : public Event { public: - static PassRefPtr<WebKitTransitionEvent> create() - { - return adoptRef(new WebKitTransitionEvent); - } - static PassRefPtr<WebKitTransitionEvent> create(const AtomicString& type, const String& propertyName, double elapsedTime, const String& pseudoElement) + static Ref<WebKitTransitionEvent> create(const AtomicString& type, const String& propertyName, double elapsedTime, const String& pseudoElement) { - return adoptRef(new WebKitTransitionEvent(type, propertyName, elapsedTime, pseudoElement)); + return adoptRef(*new WebKitTransitionEvent(type, propertyName, elapsedTime, pseudoElement)); } - static PassRefPtr<WebKitTransitionEvent> create(const AtomicString& type, const WebKitTransitionEventInit& initializer) + + struct Init : EventInit { + String propertyName; + double elapsedTime { 0 }; + String pseudoElement; + }; + + static Ref<WebKitTransitionEvent> create(const AtomicString& type, const Init& initializer, IsTrusted isTrusted = IsTrusted::No) { - return adoptRef(new WebKitTransitionEvent(type, initializer)); + return adoptRef(*new WebKitTransitionEvent(type, initializer, isTrusted)); } virtual ~WebKitTransitionEvent(); @@ -59,12 +53,11 @@ public: double elapsedTime() const; const String& pseudoElement() const; - virtual EventInterface eventInterface() const override; + EventInterface eventInterface() const override; private: - WebKitTransitionEvent(); WebKitTransitionEvent(const AtomicString& type, const String& propertyName, double elapsedTime, const String& pseudoElement); - WebKitTransitionEvent(const AtomicString& type, const WebKitTransitionEventInit& initializer); + WebKitTransitionEvent(const AtomicString& type, const Init& initializer, IsTrusted); String m_propertyName; double m_elapsedTime; @@ -72,5 +65,3 @@ private: }; } // namespace WebCore - -#endif // WebKitTransitionEvent_h diff --git a/Source/WebCore/dom/WebKitTransitionEvent.idl b/Source/WebCore/dom/WebKitTransitionEvent.idl index 4e2103008..3d57e4682 100644 --- a/Source/WebCore/dom/WebKitTransitionEvent.idl +++ b/Source/WebCore/dom/WebKitTransitionEvent.idl @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,10 +24,15 @@ */ [ - ConstructorTemplate=Event + Constructor(DOMString type, optional WebKitTransitionEventInit eventInitDict), ] interface WebKitTransitionEvent : Event { - [InitializedByEventConstructor] readonly attribute DOMString propertyName; - [InitializedByEventConstructor] readonly attribute double elapsedTime; - [InitializedByEventConstructor] readonly attribute DOMString pseudoElement; + readonly attribute DOMString propertyName; + readonly attribute double elapsedTime; + readonly attribute DOMString pseudoElement; }; +dictionary WebKitTransitionEventInit : EventInit { + DOMString propertyName = ""; + double elapsedTime = 0; + DOMString pseudoElement = ""; +}; diff --git a/Source/WebCore/dom/WheelEvent.cpp b/Source/WebCore/dom/WheelEvent.cpp index ee672cbb2..530e6b011 100644 --- a/Source/WebCore/dom/WheelEvent.cpp +++ b/Source/WebCore/dom/WheelEvent.cpp @@ -24,35 +24,23 @@ #include "config.h" #include "WheelEvent.h" -#include "Clipboard.h" +#include "DataTransfer.h" #include "EventNames.h" -#include "PlatformWheelEvent.h" - #include <wtf/MathExtras.h> namespace WebCore { -WheelEventInit::WheelEventInit() - : deltaX(0) - , deltaY(0) - , deltaZ(0) - , deltaMode(WheelEvent::DOM_DELTA_PIXEL) - , wheelDeltaX(0) - , wheelDeltaY(0) +inline static unsigned determineDeltaMode(const PlatformWheelEvent& event) { + return event.granularity() == ScrollByPageWheelEvent ? WheelEvent::DOM_DELTA_PAGE : WheelEvent::DOM_DELTA_PIXEL; } WheelEvent::WheelEvent() - : m_deltaX(0) - , m_deltaY(0) - , m_deltaZ(0) - , m_deltaMode(DOM_DELTA_PIXEL) - , m_directionInvertedFromDevice(false) { } -WheelEvent::WheelEvent(const AtomicString& type, const WheelEventInit& initializer) - : MouseEvent(type, initializer) +WheelEvent::WheelEvent(const AtomicString& type, const Init& initializer, IsTrusted isTrusted) + : MouseEvent(type, initializer, isTrusted) , m_wheelDelta(initializer.wheelDeltaX ? initializer.wheelDeltaX : -initializer.deltaX, initializer.wheelDeltaY ? initializer.wheelDeltaY : -initializer.deltaY) , m_deltaX(initializer.deltaX ? initializer.deltaX : -initializer.wheelDeltaX) , m_deltaY(initializer.deltaY ? initializer.deltaY : -initializer.wheelDeltaY) @@ -61,57 +49,42 @@ WheelEvent::WheelEvent(const AtomicString& type, const WheelEventInit& initializ { } -WheelEvent::WheelEvent(const FloatPoint& wheelTicks, const FloatPoint& rawDelta, unsigned deltaMode, - PassRefPtr<AbstractView> view, const IntPoint& screenLocation, const IntPoint& pageLocation, - bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, bool directionInvertedFromDevice, double timestamp) - : MouseEvent(eventNames().wheelEvent, - true, true, timestamp, view, 0, screenLocation.x(), screenLocation.y(), - pageLocation.x(), pageLocation.y(), +WheelEvent::WheelEvent(const PlatformWheelEvent& event, DOMWindow* view) + : MouseEvent(eventNames().wheelEvent, true, true, event.timestamp(), view, 0, event.globalPosition(), event.position() #if ENABLE(POINTER_LOCK) - 0, 0, + , { } #endif - ctrlKey, altKey, shiftKey, metaKey, 0, 0, 0, false) - , m_wheelDelta(wheelTicks.x() * TickMultiplier, wheelTicks.y() * TickMultiplier) - , m_deltaX(-rawDelta.x()) - , m_deltaY(-rawDelta.y()) - , m_deltaZ(0) - , m_deltaMode(deltaMode) - , m_directionInvertedFromDevice(directionInvertedFromDevice) + , event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey(), 0, 0, 0, 0, 0, false) + , m_wheelDelta(event.wheelTicksX() * TickMultiplier, event.wheelTicksY() * TickMultiplier) + , m_deltaX(-event.deltaX()) + , m_deltaY(-event.deltaY()) + , m_deltaMode(determineDeltaMode(event)) + , m_wheelEvent(event) + , m_initializedWithPlatformWheelEvent(true) { } -void WheelEvent::initWheelEvent(int rawDeltaX, int rawDeltaY, PassRefPtr<AbstractView> view, - int screenX, int screenY, int pageX, int pageY, - bool ctrlKey, bool altKey, bool shiftKey, bool metaKey) +void WheelEvent::initWheelEvent(int rawDeltaX, int rawDeltaY, DOMWindow* view, int screenX, int screenY, int pageX, int pageY, bool ctrlKey, bool altKey, bool shiftKey, bool metaKey) { if (dispatched()) return; initUIEvent(eventNames().wheelEvent, true, true, view, 0); - m_screenLocation = IntPoint(screenX, screenY); + m_screenLocation = { screenX, screenY }; m_ctrlKey = ctrlKey; m_altKey = altKey; m_shiftKey = shiftKey; m_metaKey = metaKey; // Normalize to 120 multiple for compatibility with IE. - m_wheelDelta = IntPoint(rawDeltaX * TickMultiplier, rawDeltaY * TickMultiplier); + m_wheelDelta = { rawDeltaX * TickMultiplier, rawDeltaY * TickMultiplier }; m_deltaX = -rawDeltaX; m_deltaY = -rawDeltaY; m_deltaMode = DOM_DELTA_PIXEL; - m_directionInvertedFromDevice = false; - - initCoordinates(IntPoint(pageX, pageY)); -} -void WheelEvent::initWebKitWheelEvent(int rawDeltaX, int rawDeltaY, PassRefPtr<AbstractView> view, - int screenX, int screenY, int pageX, int pageY, - bool ctrlKey, bool altKey, bool shiftKey, bool metaKey) -{ - initWheelEvent(rawDeltaX, rawDeltaY, view, screenX, screenY, pageX, pageY, - ctrlKey, altKey, shiftKey, metaKey); + initCoordinates({ pageX, pageY }); } EventInterface WheelEvent::eventInterface() const @@ -119,9 +92,9 @@ EventInterface WheelEvent::eventInterface() const return WheelEventInterfaceType; } -bool WheelEvent::isMouseEvent() const +bool WheelEvent::isWheelEvent() const { - return false; + return true; } } // namespace WebCore diff --git a/Source/WebCore/dom/WheelEvent.h b/Source/WebCore/dom/WheelEvent.h index 07c030e3f..b82e8ba0f 100644 --- a/Source/WebCore/dom/WheelEvent.h +++ b/Source/WebCore/dom/WheelEvent.h @@ -22,64 +22,52 @@ * */ -#ifndef WheelEvent_h -#define WheelEvent_h +#pragma once -#include "FloatPoint.h" #include "MouseEvent.h" +#include "PlatformWheelEvent.h" namespace WebCore { -class PlatformWheelEvent; - -struct WheelEventInit : public MouseEventInit { - WheelEventInit(); - - double deltaX; - double deltaY; - double deltaZ; - unsigned deltaMode; - int wheelDeltaX; // Deprecated. - int wheelDeltaY; // Deprecated. -}; - -class WheelEvent : public MouseEvent { +class WheelEvent final : public MouseEvent { public: enum { TickMultiplier = 120 }; - enum DeltaMode { + enum { DOM_DELTA_PIXEL = 0, DOM_DELTA_LINE, DOM_DELTA_PAGE }; - static PassRefPtr<WheelEvent> create() + static Ref<WheelEvent> create(const PlatformWheelEvent& event, DOMWindow* view) { - return adoptRef(new WheelEvent); + return adoptRef(*new WheelEvent(event, view)); } - static PassRefPtr<WheelEvent> create(const AtomicString& type, const WheelEventInit& initializer) + static Ref<WheelEvent> createForBindings() { - return adoptRef(new WheelEvent(type, initializer)); + return adoptRef(*new WheelEvent); } - static PassRefPtr<WheelEvent> create(const FloatPoint& wheelTicks, - const FloatPoint& rawDelta, unsigned deltaMode, PassRefPtr<AbstractView> view, - const IntPoint& screenLocation, const IntPoint& pageLocation, - bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, bool directionInvertedFromDevice, double timestamp) + struct Init : MouseEventInit { + double deltaX { 0 }; + double deltaY { 0 }; + double deltaZ { 0 }; + unsigned deltaMode { DOM_DELTA_PIXEL }; + int wheelDeltaX { 0 }; // Deprecated. + int wheelDeltaY { 0 }; // Deprecated. + }; + + static Ref<WheelEvent> create(const AtomicString& type, const Init& initializer, IsTrusted isTrusted = IsTrusted::No) { - return adoptRef(new WheelEvent(wheelTicks, rawDelta, deltaMode, view, - screenLocation, pageLocation, ctrlKey, altKey, shiftKey, metaKey, directionInvertedFromDevice, timestamp)); + return adoptRef(*new WheelEvent(type, initializer, isTrusted)); } - void initWheelEvent(int rawDeltaX, int rawDeltaY, PassRefPtr<AbstractView>, - int screenX, int screenY, int pageX, int pageY, - bool ctrlKey, bool altKey, bool shiftKey, bool metaKey); + WEBCORE_EXPORT void initWheelEvent(int rawDeltaX, int rawDeltaY, DOMWindow*, int screenX, int screenY, int pageX, int pageY, bool ctrlKey, bool altKey, bool shiftKey, bool metaKey); - void initWebKitWheelEvent(int rawDeltaX, int rawDeltaY, PassRefPtr<AbstractView>, - int screenX, int screenY, int pageX, int pageY, - bool ctrlKey, bool altKey, bool shiftKey, bool metaKey); + void initWebKitWheelEvent(int rawDeltaX, int rawDeltaY, DOMWindow*, int screenX, int screenY, int pageX, int pageY, bool ctrlKey, bool altKey, bool shiftKey, bool metaKey); + const PlatformWheelEvent* wheelEvent() const { return m_initializedWithPlatformWheelEvent ? &m_wheelEvent : nullptr; } double deltaX() const { return m_deltaX; } // Positive when scrolling right. double deltaY() const { return m_deltaY; } // Positive when scrolling down. double deltaZ() const { return m_deltaZ; } @@ -88,28 +76,36 @@ public: int wheelDeltaY() const { return m_wheelDelta.y(); } // Deprecated, negative when scrolling down. unsigned deltaMode() const { return m_deltaMode; } - bool webkitDirectionInvertedFromDevice() const { return m_directionInvertedFromDevice; } - // Needed for Objective-C legacy support - bool isHorizontal() const { return m_wheelDelta.x(); } + bool webkitDirectionInvertedFromDevice() const { return m_wheelEvent.directionInvertedFromDevice(); } - virtual EventInterface eventInterface() const override; - virtual bool isMouseEvent() const override; +#if PLATFORM(MAC) + PlatformWheelEventPhase phase() const { return m_wheelEvent.phase(); } + PlatformWheelEventPhase momentumPhase() const { return m_wheelEvent.momentumPhase(); } +#endif private: WheelEvent(); - WheelEvent(const AtomicString&, const WheelEventInit&); - WheelEvent(const FloatPoint& wheelTicks, const FloatPoint& rawDelta, - unsigned, PassRefPtr<AbstractView>, const IntPoint& screenLocation, const IntPoint& pageLocation, - bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, bool directionInvertedFromDevice, double timestamp); + WheelEvent(const AtomicString&, const Init&, IsTrusted); + WheelEvent(const PlatformWheelEvent&, DOMWindow*); + + EventInterface eventInterface() const final; + + bool isWheelEvent() const final; IntPoint m_wheelDelta; - double m_deltaX; - double m_deltaY; - double m_deltaZ; - unsigned m_deltaMode; - bool m_directionInvertedFromDevice; + double m_deltaX { 0 }; + double m_deltaY { 0 }; + double m_deltaZ { 0 }; + unsigned m_deltaMode { DOM_DELTA_PIXEL }; + PlatformWheelEvent m_wheelEvent; + bool m_initializedWithPlatformWheelEvent { false }; }; +inline void WheelEvent::initWebKitWheelEvent(int rawDeltaX, int rawDeltaY, DOMWindow* view, int screenX, int screenY, int pageX, int pageY, bool ctrlKey, bool altKey, bool shiftKey, bool metaKey) +{ + initWheelEvent(rawDeltaX, rawDeltaY, view, screenX, screenY, pageX, pageY, ctrlKey, altKey, shiftKey, metaKey); +} + } // namespace WebCore -#endif // WheelEvent_h +SPECIALIZE_TYPE_TRAITS_EVENT(WheelEvent) diff --git a/Source/WebCore/dom/WheelEvent.idl b/Source/WebCore/dom/WheelEvent.idl index 8eb4fa7eb..93c53a4bf 100644 --- a/Source/WebCore/dom/WheelEvent.idl +++ b/Source/WebCore/dom/WheelEvent.idl @@ -19,58 +19,39 @@ * Boston, MA 02110-1301, USA. */ -// Introduced in DOM Level 3: [ - ConstructorConditional=DOM4_EVENTS_CONSTRUCTOR, - ConstructorTemplate=Event, + Constructor(DOMString type, optional WheelEventInit eventInitDict), ] interface WheelEvent : MouseEvent { // DeltaModeCode - const unsigned long DOM_DELTA_PIXEL = 0x00; - const unsigned long DOM_DELTA_LINE = 0x01; - const unsigned long DOM_DELTA_PAGE = 0x02; + const unsigned long DOM_DELTA_PIXEL = 0x00; + const unsigned long DOM_DELTA_LINE = 0x01; + const unsigned long DOM_DELTA_PAGE = 0x02; - [InitializedByEventConstructor] readonly attribute double deltaX; - [InitializedByEventConstructor] readonly attribute double deltaY; - [InitializedByEventConstructor] readonly attribute double deltaZ; - [InitializedByEventConstructor] readonly attribute unsigned long deltaMode; + readonly attribute unrestricted double deltaX; + readonly attribute unrestricted double deltaY; + readonly attribute unrestricted double deltaZ; + readonly attribute unsigned long deltaMode; // Legacy MouseWheelEvent API replaced by standard WheelEvent API. - [InitializedByEventConstructor] readonly attribute long wheelDeltaX; - [InitializedByEventConstructor] readonly attribute long wheelDeltaY; + readonly attribute long wheelDeltaX; + readonly attribute long wheelDeltaY; readonly attribute long wheelDelta; - // WebKit Extension readonly attribute boolean webkitDirectionInvertedFromDevice; -#if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C - readonly attribute boolean isHorizontal; -#endif /* defined(LANGUAGE_OBJECTIVE_C) */ + void initWebKitWheelEvent(optional long wheelDeltaX = 0, optional long wheelDeltaY = 0, optional DOMWindow? view = null, + optional long screenX = 0, optional long screenY = 0, optional long clientX = 0, optional long clientY = 0, + optional boolean ctrlKey = false, optional boolean altKey = false, + optional boolean shiftKey = false, optional boolean metaKey = false); +}; -#if !defined(LANGUAGE_JAVASCRIPT) || !LANGUAGE_JAVASCRIPT - void initWheelEvent([Default=Undefined] optional long wheelDeltaX, - [Default=Undefined] optional long wheelDeltaY, - [Default=Undefined] optional DOMWindow view, - [Default=Undefined] optional long screenX, - [Default=Undefined] optional long screenY, - [Default=Undefined] optional long clientX, - [Default=Undefined] optional long clientY, - [Default=Undefined] optional boolean ctrlKey, - [Default=Undefined] optional boolean altKey, - [Default=Undefined] optional boolean shiftKey, - [Default=Undefined] optional boolean metaKey); -#endif /* !defined(LANGUAGE_JAVASCRIPT) */ +dictionary WheelEventInit : MouseEventInit { + double deltaX = 0.0; + double deltaY = 0.0; + double deltaZ = 0.0; + unsigned long deltaMode = 0; -#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT - void initWebKitWheelEvent([Default=Undefined] optional long wheelDeltaX, - [Default=Undefined] optional long wheelDeltaY, - [Default=Undefined] optional DOMWindow view, - [Default=Undefined] optional long screenX, - [Default=Undefined] optional long screenY, - [Default=Undefined] optional long clientX, - [Default=Undefined] optional long clientY, - [Default=Undefined] optional boolean ctrlKey, - [Default=Undefined] optional boolean altKey, - [Default=Undefined] optional boolean shiftKey, - [Default=Undefined] optional boolean metaKey); -#endif /* defined(LANGUAGE_JAVASCRIPT) */ + // Legacy members that we still support for backward compatibility. + long wheelDeltaX = 0; + long wheelDeltaY = 0; }; diff --git a/Source/WebCore/dom/XMLDocument.h b/Source/WebCore/dom/XMLDocument.h new file mode 100644 index 000000000..461b8c6eb --- /dev/null +++ b/Source/WebCore/dom/XMLDocument.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "Document.h" + +namespace WebCore { + +class XMLDocument : public Document { +public: + static Ref<XMLDocument> create(Frame* frame, const URL& url) + { + return adoptRef(*new XMLDocument(frame, url)); + } + + static Ref<XMLDocument> createXHTML(Frame* frame, const URL& url) + { + return adoptRef(*new XMLDocument(frame, url, XHTMLDocumentClass)); + } + +protected: + XMLDocument(Frame* frame, const URL& url, unsigned documentClasses = DefaultDocumentClass) + : Document(frame, url, XMLDocumentClass | documentClasses) + { } +}; + +} // namespace WebCore + +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::XMLDocument) + static bool isType(const WebCore::Document& document) { return document.isXMLDocument(); } + static bool isType(const WebCore::Node& node) { return is<WebCore::Document>(node) && isType(downcast<WebCore::Document>(node)); } +SPECIALIZE_TYPE_TRAITS_END() diff --git a/Source/WebCore/dom/XMLDocument.idl b/Source/WebCore/dom/XMLDocument.idl new file mode 100644 index 000000000..38f1278f8 --- /dev/null +++ b/Source/WebCore/dom/XMLDocument.idl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +[ + CustomToJSObject, +] interface XMLDocument : Document +{ +}; diff --git a/Source/WebCore/dom/default/PlatformMessagePortChannel.cpp b/Source/WebCore/dom/default/PlatformMessagePortChannel.cpp index 902da1834..9be0c65bd 100644 --- a/Source/WebCore/dom/default/PlatformMessagePortChannel.cpp +++ b/Source/WebCore/dom/default/PlatformMessagePortChannel.cpp @@ -37,34 +37,23 @@ namespace WebCore { -std::unique_ptr<PlatformMessagePortChannel::EventData> PlatformMessagePortChannel::EventData::create(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels) +void MessagePortChannel::createChannel(MessagePort* port1, MessagePort* port2) { - return std::unique_ptr<EventData>(new EventData(message, channels)); -} - -PlatformMessagePortChannel::EventData::EventData(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels) - : m_message(message) - , m_channels(channels) -{ -} - -void MessagePortChannel::createChannel(PassRefPtr<MessagePort> port1, PassRefPtr<MessagePort> port2) -{ - RefPtr<PlatformMessagePortChannel::MessagePortQueue> queue1 = PlatformMessagePortChannel::MessagePortQueue::create(); - RefPtr<PlatformMessagePortChannel::MessagePortQueue> queue2 = PlatformMessagePortChannel::MessagePortQueue::create(); + Ref<PlatformMessagePortChannel::MessagePortQueue> queue1 = PlatformMessagePortChannel::MessagePortQueue::create(); + Ref<PlatformMessagePortChannel::MessagePortQueue> queue2 = PlatformMessagePortChannel::MessagePortQueue::create(); - OwnPtr<MessagePortChannel> channel1 = adoptPtr(new MessagePortChannel(PlatformMessagePortChannel::create(queue1, queue2))); - OwnPtr<MessagePortChannel> channel2 = adoptPtr(new MessagePortChannel(PlatformMessagePortChannel::create(queue2, queue1))); + auto channel1 = std::make_unique<MessagePortChannel>(PlatformMessagePortChannel::create(queue1.ptr(), queue2.ptr())); + auto channel2 = std::make_unique<MessagePortChannel>(PlatformMessagePortChannel::create(queue2.ptr(), queue1.ptr())); channel1->m_channel->m_entangledChannel = channel2->m_channel; channel2->m_channel->m_entangledChannel = channel1->m_channel; - port1->entangle(channel2.release()); - port2->entangle(channel1.release()); + port1->entangle(WTFMove(channel2)); + port2->entangle(WTFMove(channel1)); } -MessagePortChannel::MessagePortChannel(PassRefPtr<PlatformMessagePortChannel> channel) - : m_channel(channel) +MessagePortChannel::MessagePortChannel(RefPtr<PlatformMessagePortChannel>&& channel) + : m_channel(WTFMove(channel)) { } @@ -88,30 +77,29 @@ void MessagePortChannel::disentangle() { RefPtr<PlatformMessagePortChannel> remote = m_channel->entangledChannel(); if (remote) - remote->setRemotePort(0); + remote->setRemotePort(nullptr); } -void MessagePortChannel::postMessageToRemote(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels) +void MessagePortChannel::postMessageToRemote(Ref<SerializedScriptValue>&& message, std::unique_ptr<MessagePortChannelArray> channels) { - MutexLocker lock(m_channel->m_mutex); + LockHolder lock(m_channel->m_mutex); if (!m_channel->m_outgoingQueue) return; - bool wasEmpty = m_channel->m_outgoingQueue->appendAndCheckEmpty(PlatformMessagePortChannel::EventData::create(message, channels)); + bool wasEmpty = m_channel->m_outgoingQueue->appendAndCheckEmpty(std::make_unique<EventData>(WTFMove(message), WTFMove(channels))); if (wasEmpty && m_channel->m_remotePort) m_channel->m_remotePort->messageAvailable(); } -bool MessagePortChannel::tryGetMessageFromRemote(RefPtr<SerializedScriptValue>& message, OwnPtr<MessagePortChannelArray>& channels) +auto MessagePortChannel::takeMessageFromRemote() -> std::unique_ptr<EventData> { - MutexLocker lock(m_channel->m_mutex); - auto result = m_channel->m_incomingQueue->tryGetMessage(); - if (!result) - return false; - - message = result->message(); - channels = result->channels(); + LockHolder lock(m_channel->m_mutex); + return m_channel->m_incomingQueue->takeMessage(); +} - return true; +auto MessagePortChannel::takeAllMessagesFromRemote() -> Deque<std::unique_ptr<EventData>> +{ + LockHolder lock(m_channel->m_mutex); + return m_channel->m_incomingQueue->takeAllMessages(); } void MessagePortChannel::close() @@ -126,20 +114,20 @@ void MessagePortChannel::close() bool MessagePortChannel::isConnectedTo(MessagePort* port) { // FIXME: What guarantees that the result remains the same after we release the lock? - MutexLocker lock(m_channel->m_mutex); + LockHolder lock(m_channel->m_mutex); return m_channel->m_remotePort == port; } bool MessagePortChannel::hasPendingActivity() { // FIXME: What guarantees that the result remains the same after we release the lock? - MutexLocker lock(m_channel->m_mutex); + LockHolder lock(m_channel->m_mutex); return !m_channel->m_incomingQueue->isEmpty(); } MessagePort* MessagePortChannel::locallyEntangledPort(const ScriptExecutionContext* context) { - MutexLocker lock(m_channel->m_mutex); + LockHolder lock(m_channel->m_mutex); // See if both contexts are run by the same thread (are the same context, or are both documents). if (m_channel->m_remotePort) { // The remote port's ScriptExecutionContext is guaranteed not to change here - MessagePort::contextDestroyed() @@ -151,15 +139,14 @@ MessagePort* MessagePortChannel::locallyEntangledPort(const ScriptExecutionConte return 0; } -PassRefPtr<PlatformMessagePortChannel> PlatformMessagePortChannel::create(PassRefPtr<MessagePortQueue> incoming, PassRefPtr<MessagePortQueue> outgoing) +Ref<PlatformMessagePortChannel> PlatformMessagePortChannel::create(MessagePortQueue* incoming, MessagePortQueue* outgoing) { - return adoptRef(new PlatformMessagePortChannel(incoming, outgoing)); + return adoptRef(*new PlatformMessagePortChannel(incoming, outgoing)); } -PlatformMessagePortChannel::PlatformMessagePortChannel(PassRefPtr<MessagePortQueue> incoming, PassRefPtr<MessagePortQueue> outgoing) +PlatformMessagePortChannel::PlatformMessagePortChannel(MessagePortQueue* incoming, MessagePortQueue* outgoing) : m_incomingQueue(incoming) , m_outgoingQueue(outgoing) - , m_remotePort(0) { } @@ -169,28 +156,28 @@ PlatformMessagePortChannel::~PlatformMessagePortChannel() void PlatformMessagePortChannel::setRemotePort(MessagePort* port) { - MutexLocker lock(m_mutex); + LockHolder lock(m_mutex); // Should never set port if it is already set. ASSERT(!port || !m_remotePort); m_remotePort = port; } -PassRefPtr<PlatformMessagePortChannel> PlatformMessagePortChannel::entangledChannel() +RefPtr<PlatformMessagePortChannel> PlatformMessagePortChannel::entangledChannel() { // FIXME: What guarantees that the result remains the same after we release the lock? // This lock only guarantees that the returned pointer will not be pointing to released memory, // but not that it will still be pointing to this object's entangled port channel. - MutexLocker lock(m_mutex); + LockHolder lock(m_mutex); return m_entangledChannel; } void PlatformMessagePortChannel::closeInternal() { - MutexLocker lock(m_mutex); + LockHolder lock(m_mutex); // Disentangle ourselves from the other end. We still maintain a reference to our incoming queue, since previously-existing messages should still be delivered. - m_remotePort = 0; - m_entangledChannel = 0; - m_outgoingQueue = 0; + m_remotePort = nullptr; + m_entangledChannel = nullptr; + m_outgoingQueue = nullptr; } } // namespace WebCore diff --git a/Source/WebCore/dom/default/PlatformMessagePortChannel.h b/Source/WebCore/dom/default/PlatformMessagePortChannel.h index 6eb89d47d..4e4916a4f 100644 --- a/Source/WebCore/dom/default/PlatformMessagePortChannel.h +++ b/Source/WebCore/dom/default/PlatformMessagePortChannel.h @@ -28,13 +28,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PlatformMessagePortChannel_h -#define PlatformMessagePortChannel_h +#pragma once #include "MessagePortChannel.h" - #include <wtf/MessageQueue.h> -#include <wtf/PassRefPtr.h> #include <wtf/Threading.h> namespace WebCore { @@ -46,33 +43,24 @@ namespace WebCore { // The goal of this implementation is to eliminate contention except when cloning or closing the port, so each side of the channel has its own separate mutex. class PlatformMessagePortChannel : public ThreadSafeRefCounted<PlatformMessagePortChannel> { public: - class EventData { - WTF_MAKE_NONCOPYABLE(EventData); WTF_MAKE_FAST_ALLOCATED; - public: - static std::unique_ptr<EventData> create(PassRefPtr<SerializedScriptValue>, PassOwnPtr<MessagePortChannelArray>); - - PassRefPtr<SerializedScriptValue> message() { return m_message; } - PassOwnPtr<MessagePortChannelArray> channels() { return m_channels.release(); } - - private: - EventData(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray>); - RefPtr<SerializedScriptValue> m_message; - OwnPtr<MessagePortChannelArray> m_channels; - }; - // Wrapper for MessageQueue that allows us to do thread safe sharing by two proxies. class MessagePortQueue : public ThreadSafeRefCounted<MessagePortQueue> { public: - static PassRefPtr<MessagePortQueue> create() { return adoptRef(new MessagePortQueue()); } + static Ref<MessagePortQueue> create() { return adoptRef(*new MessagePortQueue()); } - std::unique_ptr<PlatformMessagePortChannel::EventData> tryGetMessage() + std::unique_ptr<MessagePortChannel::EventData> takeMessage() { return m_queue.tryGetMessage(); } - bool appendAndCheckEmpty(std::unique_ptr<PlatformMessagePortChannel::EventData> message) + Deque<std::unique_ptr<MessagePortChannel::EventData>> takeAllMessages() { - return m_queue.appendAndCheckEmpty(std::move(message)); + return m_queue.takeAllMessages(); + } + + bool appendAndCheckEmpty(std::unique_ptr<MessagePortChannel::EventData>&& message) + { + return m_queue.appendAndCheckEmpty(WTFMove(message)); } bool isEmpty() @@ -83,21 +71,21 @@ namespace WebCore { private: MessagePortQueue() { } - MessageQueue<PlatformMessagePortChannel::EventData> m_queue; + MessageQueue<MessagePortChannel::EventData> m_queue; }; ~PlatformMessagePortChannel(); - static PassRefPtr<PlatformMessagePortChannel> create(PassRefPtr<MessagePortQueue> incoming, PassRefPtr<MessagePortQueue> outgoing); - PlatformMessagePortChannel(PassRefPtr<MessagePortQueue> incoming, PassRefPtr<MessagePortQueue> outgoing); + static Ref<PlatformMessagePortChannel> create(MessagePortQueue* incoming, MessagePortQueue* outgoing); + PlatformMessagePortChannel(MessagePortQueue* incoming, MessagePortQueue* outgoing); - PassRefPtr<PlatformMessagePortChannel> entangledChannel(); + RefPtr<PlatformMessagePortChannel> entangledChannel(); void setRemotePort(MessagePort*); void closeInternal(); - // Mutex used to ensure exclusive access to the object internals. - Mutex m_mutex; + // Lock used to ensure exclusive access to the object internals. + Lock m_mutex; // Pointer to our entangled pair - cleared when close() is called. RefPtr<PlatformMessagePortChannel> m_entangledChannel; @@ -107,9 +95,7 @@ namespace WebCore { RefPtr<MessagePortQueue> m_outgoingQueue; // The port we are connected to (the remote port) - this is the port that is notified when new messages arrive. - MessagePort* m_remotePort; + MessagePort* m_remotePort { nullptr }; }; } // namespace WebCore - -#endif // PlatformMessagePortChannel_h diff --git a/Source/WebCore/dom/make_dom_exceptions.pl b/Source/WebCore/dom/make_dom_exceptions.pl index e12c81c95..726d08fa7 100644 --- a/Source/WebCore/dom/make_dom_exceptions.pl +++ b/Source/WebCore/dom/make_dom_exceptions.pl @@ -1,6 +1,6 @@ #!/usr/bin/perl -w -# Copyright (C) 2005, 2006, 2007, 2009 Apple Inc. All rights reserved. +# Copyright (C) 2005, 2006, 2007, 2009, 2016 Apple Inc. All rights reserved. # Copyright (C) 2009, Julien Chaffraix <jchaffraix@webkit.org> # Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) # Copyright (C) 2011 Ericsson AB. All rights reserved. @@ -15,7 +15,7 @@ # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. -# 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of +# 3. Neither the name of Apple Inc. ("Apple") nor the names of # its contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # @@ -31,6 +31,8 @@ # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use strict; +use FindBin; +use lib "$FindBin::Bin/../bindings/scripts"; use InFilesCompiler; @@ -76,12 +78,11 @@ sub generateHeader() print F $InCompiler->license(); - print F "#ifndef ExceptionCodeDescription_h\n"; - print F "#define ExceptionCodeDescription_h\n"; + print F "#pragma once\n"; print F "\n"; print F "namespace WebCore {\n"; print F "\n"; - print F "typedef int ExceptionCode;\n"; + print F "using ExceptionCode = int;\n"; print F "\n"; print F "enum ExceptionType {\n"; @@ -93,10 +94,13 @@ sub generateHeader() print F "#endif\n" if $conditional; } + print F "#if ENABLE(INDEXED_DATABASE)\n"; + print F " IDBDatabaseExceptionType,\n"; + print F "#endif\n"; print F "};\n"; print F "\n"; print F "struct ExceptionCodeDescription {\n"; - print F " explicit ExceptionCodeDescription(ExceptionCode);\n"; + print F " WEBCORE_EXPORT explicit ExceptionCodeDescription(ExceptionCode);\n"; print F "\n"; print F " // |typeName| has spaces and is suitable for use in exception\n"; print F " // description strings; maximum length is 10 characters.\n"; @@ -118,8 +122,6 @@ sub generateHeader() print F "};\n"; print F "\n"; print F "} // namespace WebCore\n"; - print F "\n"; - print F "#endif // ExceptionCodeDescription_h\n"; close F; } diff --git a/Source/WebCore/dom/make_event_factory.pl b/Source/WebCore/dom/make_event_factory.pl index 9331b452c..705f2df01 100644 --- a/Source/WebCore/dom/make_event_factory.pl +++ b/Source/WebCore/dom/make_event_factory.pl @@ -15,7 +15,7 @@ # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. -# 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of +# 3. Neither the name of Apple Inc. ("Apple") nor the names of # its contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # @@ -31,6 +31,8 @@ # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use strict; +use FindBin; +use lib "$FindBin::Bin/../bindings/scripts"; use InFilesCompiler; @@ -67,7 +69,6 @@ sub generateImplementation() my $parsedParametersRef = shift; my $parsedItemsRef = shift; - my $F; my %parsedEvents = %{ $parsedItemsRef }; my %parsedParameters = %{ $parsedParametersRef }; @@ -76,9 +77,8 @@ sub generateImplementation() # Currently, only Events have factory files. return if $namespace ne "Event"; - my $outputFile = "$outputDir/${namespace}Factory.cpp"; - - open F, ">$outputFile" or die "Failed to open file: $!"; + my $F; + open F, ">", "$outputDir/${namespace}Factory.cpp" or die "Failed to open file: $!"; print F $InCompiler->license(); @@ -86,10 +86,11 @@ sub generateImplementation() print F "#include \"${namespace}Factory.h\"\n"; print F "\n"; print F "#include \"${namespace}Headers.h\"\n"; + print F "#include <runtime/StructureInlines.h>\n"; print F "\n"; print F "namespace WebCore {\n"; print F "\n"; - print F "PassRefPtr<$namespace> ${namespace}Factory::create(const String& type)\n"; + print F "RefPtr<$namespace> ${namespace}Factory::create(const String& type)\n"; print F "{\n"; for my $eventName (sort keys %parsedEvents) { @@ -97,14 +98,18 @@ sub generateImplementation() my $runtimeConditional = $parsedEvents{$eventName}{"runtimeConditional"}; my $interfaceName = $InCompiler->interfaceForItem($eventName); - print F "#if ENABLE($conditional)\n" if $conditional; - # FIXEME JSC should support RuntimeEnabledFeatures - print F " if (type == \"$eventName\")\n"; + # FIXME: This should pay attention to $runtimeConditional so it can support RuntimeEnabledFeatures. + + if ($conditional) { + my $conditionals = "#if ENABLE(" . join(") || ENABLE(", split("\\|", $conditional)) . ")"; + print F "$conditionals\n"; + } + print F " if (equalIgnoringASCIICase(type, \"$eventName\"))\n"; print F " return ${interfaceName}::create();\n"; print F "#endif\n" if $conditional; } - print F " return 0;\n"; + print F " return nullptr;\n"; print F "}\n"; print F "\n"; print F "} // namespace WebCore\n"; diff --git a/Source/WebCore/dom/make_names.pl b/Source/WebCore/dom/make_names.pl index 3bc298325..071f22d35 100755 --- a/Source/WebCore/dom/make_names.pl +++ b/Source/WebCore/dom/make_names.pl @@ -1,6 +1,6 @@ #!/usr/bin/perl -w -# Copyright (C) 2005, 2006, 2007, 2009, 2013 Apple Inc. All rights reserved. +# Copyright (C) 2005-2007, 2009, 2013-2014 Apple Inc. All rights reserved. # Copyright (C) 2009, Julien Chaffraix <jchaffraix@webkit.org> # Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) # Copyright (C) 2011 Ericsson AB. All rights reserved. @@ -14,7 +14,7 @@ # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. -# 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of +# 3. Neither the name of Apple Inc. ("Apple") nor the names of # its contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # @@ -30,6 +30,8 @@ # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use strict; +use FindBin; +use lib "$FindBin::Bin/../bindings/scripts"; use StaticString; use Config; @@ -62,19 +64,23 @@ my %extensionAttrs = (); require Config; -my $gccLocation = ""; +my $ccLocation = ""; if ($ENV{CC}) { - $gccLocation = $ENV{CC}; + $ccLocation = $ENV{CC}; } elsif (($Config::Config{"osname"}) =~ /solaris/i) { - $gccLocation = "/usr/sfw/bin/gcc"; + $ccLocation = "/usr/sfw/bin/gcc"; } elsif ($Config::Config{"osname"} eq "darwin" && $ENV{SDKROOT}) { - chomp($gccLocation = `xcrun -find cc -sdk '$ENV{SDKROOT}'`); -} elsif ($Config::Config{"osname"} eq "msys") { - $gccLocation = "gcc"; + chomp($ccLocation = `xcrun -find cc -sdk '$ENV{SDKROOT}'`); } else { - $gccLocation = "/usr/bin/cc"; + $ccLocation = "/usr/bin/cc"; +} + +my $preprocessor = ""; +if ($Config::Config{"osname"} eq "MSWin32") { + $preprocessor = "\"$ccLocation\" /EP"; +} else { + $preprocessor = $ccLocation . " -E -x c++"; } -my $preprocessor = $gccLocation . " -E -x c++"; GetOptions( 'tags=s' => \$tagsFile, @@ -105,7 +111,7 @@ if (length($fontNamesIn)) { open F, ">$header" or die "Unable to open $header for writing."; printLicenseHeader($F); - printHeaderHead($F, "CSS", $familyNamesFileBase, "#include <wtf/text/AtomicString.h>"); + printHeaderHead($F, "CSS", $familyNamesFileBase, "#include <wtf/text/AtomicString.h>", ""); printMacros($F, "extern const WTF::AtomicString", "", \%parameters); print F "#endif\n\n"; @@ -121,7 +127,7 @@ if (length($fontNamesIn)) { print F StaticString::GenerateStrings(\%parameters); - while ( my ($name, $identifier) = each %parameters ) { + for my $name (sort keys %parameters) { print F "DEFINE_GLOBAL(AtomicString, $name)\n"; } @@ -130,7 +136,7 @@ if (length($fontNamesIn)) { print F "\n"; print F StaticString::GenerateStringAsserts(\%parameters); - while ( my ($name, $identifier) = each %parameters ) { + for my $name (sort keys %parameters) { # FIXME: Would like to use static_cast here, but there are differences in const # depending on whether SKIP_STATIC_CONSTRUCTORS_ON_GCC is used, so stick with a # C-style cast for now. @@ -194,9 +200,10 @@ sub defaultTagPropertyHash 'JSInterfaceName' => defaultInterfaceName($_[0]), 'mapToTagName' => '', 'wrapperOnlyIfMediaIsAvailable' => 0, + 'settingsConditional' => 0, 'conditional' => 0, 'runtimeConditional' => 0, - 'generateTypeHelpers' => 0 + 'customTypeHelper' => 0, ); } @@ -211,6 +218,7 @@ sub defaultParametersHash 'attrsNullNamespace' => 0, 'fallbackInterfaceName' => '', 'fallbackJSInterfaceName' => '', + 'customElementInterfaceName' => '', ); } @@ -383,7 +391,7 @@ sub printConstructorSignature { my ($F, $tagName, $constructorName, $constructorTagName) = @_; - print F "static PassRefPtr<$parameters{namespace}Element> ${constructorName}Constructor(const QualifiedName& $constructorTagName, Document& document"; + print F "static Ref<$parameters{namespace}Element> ${constructorName}Constructor(const QualifiedName& $constructorTagName, Document& document"; if ($parameters{namespace} eq "HTML") { print F ", HTMLFormElement*"; print F " formElement" if $enabledTags{$tagName}{constructorNeedsFormElement}; @@ -407,9 +415,8 @@ sub printConstructorInterior # instead of having all the support for this here in this script? if ($enabledTags{$tagName}{wrapperOnlyIfMediaIsAvailable}) { print F <<END - Settings* settings = document.settings(); - if (!MediaPlayer::isAvailable() || (settings && !settings->mediaEnabled())) - return 0; + if (!MediaPlayer::isAvailable() || !document.settings().mediaEnabled()) + return $parameters{fallbackInterfaceName}::create($constructorTagName, document); END ; @@ -424,6 +431,15 @@ END ; } + my $settingsConditional = $enabledTags{$tagName}{settingsConditional}; + if ($settingsConditional) { + print F <<END + if (!document.settings().${settingsConditional}()) + return $parameters{fallbackInterfaceName}::create($constructorTagName, document); +END +; + } + # Call the constructor with the right parameters. print F " return ${interfaceName}::create($constructorTagName, document"; print F ", formElement" if $enabledTags{$tagName}{constructorNeedsFormElement}; @@ -527,34 +543,41 @@ sub upperCaseName sub printHeaderHead { - my ($F, $prefix, $nsName, $includes) = @_; + my ($F, $prefix, $namespace, $includes, $definitions) = @_; + + print F<<END +#ifndef ${prefix}_${namespace}Names_h - print F "#ifndef ${prefix}_${nsName}Names_h\n"; - print F "#define ${prefix}_${nsName}Names_h\n\n"; - print F "$includes\n\n"; +#define ${prefix}_${namespace}Names_h - print F "namespace WebCore {\n\n"; - print F "namespace ${nsName}Names {\n\n"; +$includes + +namespace WebCore { + +${definitions}namespace ${namespace}Names { - print F "#ifndef ${prefix}_${nsName}NAMES_HIDE_GLOBALS\n"; +#ifndef ${prefix}_${namespace}_NAMES_HIDE_GLOBALS + +END + ; } sub printCppHead { - my ($F, $prefix, $nsName, $usedNamespace) = @_; + my ($F, $prefix, $namespace, $usedNamespace) = @_; print F "#include \"config.h\"\n\n"; print F "#ifdef SKIP_STATIC_CONSTRUCTORS_ON_GCC\n"; - print F "#define ${prefix}_${nsName}NAMES_HIDE_GLOBALS 1\n"; + print F "#define ${prefix}_${namespace}_NAMES_HIDE_GLOBALS 1\n"; print F "#else\n"; print F "#define QNAME_DEFAULT_CONSTRUCTOR 1\n"; print F "#endif\n\n"; - print F "#include \"${nsName}Names.h\"\n\n"; + print F "#include \"${namespace}Names.h\"\n\n"; print F "#include <wtf/StaticConstructors.h>\n"; print F "namespace WebCore {\n\n"; - print F "namespace ${nsName}Names {\n\n"; + print F "namespace ${namespace}Names {\n\n"; print F "using namespace $usedNamespace;\n\n"; } @@ -563,7 +586,7 @@ sub printInit my ($F, $isDefinition) = @_; if ($isDefinition) { - print F "\nvoid init();\n\n"; + print F "\nWEBCORE_EXPORT void init();\n\n"; print F "} }\n\n"; print F "#endif\n\n"; return; @@ -601,10 +624,10 @@ sub printLicenseHeader * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -622,57 +645,51 @@ sub printTypeHelpers my ($F, $namesRef) = @_; my %names = %$namesRef; - for my $name (sort keys %names) { - if (!$parsedTags{$name}{generateTypeHelpers}) { - next; - } - + # Do a first pass to discard classes that map to several tags. + my %classToTags = (); + for my $name (keys %names) { my $class = $parsedTags{$name}{interfaceName}; - my $checkHelper = "is$class"; + push(@{$classToTags{$class}}, $name) if defined $class; + } + + for my $class (sort keys %classToTags) { + my $name = $classToTags{$class}[0]; + next if $parsedTags{$name}{customTypeHelper}; + # Skip classes that map to more than 1 tag. + my $tagCount = scalar @{$classToTags{$class}}; + next if $tagCount > 1; print F <<END +namespace WebCore { class $class; -void $checkHelper(const $class&); // Catch unnecessary runtime check of type known at compile time. -void $checkHelper(const $class*); // Catch unnecessary runtime check of type known at compile time. -END - ; - - if ($parameters{namespace} eq "HTML") { - if ($parsedTags{$name}{wrapperOnlyIfMediaIsAvailable}) { - # We need to check for HTMLUnknownElement if it might have been created by the factory. - print F <<END -inline bool $checkHelper(const HTMLElement& element) { return !element.isHTMLUnknownElement() && element.hasLocalName($parameters{namespace}Names::${name}Tag); } -inline bool $checkHelper(const HTMLElement* element) { ASSERT(element); return $checkHelper(*element); } +} +namespace WTF { +template <typename ArgType> +class TypeCastTraits<const WebCore::$class, ArgType, false /* isBaseType */> { +public: + static bool isOfType(ArgType& node) { return checkTagName(node); } +private: END - ; - } else { - print F <<END -inline bool $checkHelper(const HTMLElement& element) { return element.hasLocalName(HTMLNames::${name}Tag); } -inline bool $checkHelper(const HTMLElement* element) { ASSERT(element); return $checkHelper(*element); } + ; + if ($parameters{namespace} eq "HTML" && ($parsedTags{$name}{wrapperOnlyIfMediaIsAvailable} || $parsedTags{$name}{settingsConditional})) { + print F <<END + static bool checkTagName(const WebCore::HTMLElement& element) { return !element.isHTMLUnknownElement() && element.hasTagName(WebCore::$parameters{namespace}Names::${name}Tag); } + static bool checkTagName(const WebCore::Node& node) { return is<WebCore::HTMLElement>(node) && checkTagName(downcast<WebCore::HTMLElement>(node)); } END - ; - } - - print F <<END -inline bool $checkHelper(const Node& node) { return node.isHTMLElement() && $checkHelper(toHTMLElement(node)); } -inline bool $checkHelper(const Node* node) { ASSERT(node); return $checkHelper(*node); } -template <> inline bool isElementOfType<const $class>(const HTMLElement& element) { return $checkHelper(element); } -template <> inline bool isElementOfType<const $class>(const Element& element) { return $checkHelper(element); } + ; + } else { + print F <<END + static bool checkTagName(const WebCore::$parameters{namespace}Element& element) { return element.hasTagName(WebCore::$parameters{namespace}Names::${name}Tag); } + static bool checkTagName(const WebCore::Node& node) { return node.hasTagName(WebCore::$parameters{namespace}Names::${name}Tag); } END - ; - - } else { - print F <<END -inline bool $checkHelper(const Element& element) { return element.hasTagName($parameters{namespace}Names::${name}Tag); } -inline bool $checkHelper(const Element* element) { ASSERT(element); return $checkHelper(*element); } -inline bool $checkHelper(const Node& node) { return node.isElementNode() && $checkHelper(toElement(node)); } -inline bool $checkHelper(const Node* node) { ASSERT(node); return node->isElementNode() && $checkHelper(toElement(node)); } -template <> inline bool isElementOfType<const $class>(const Element& element) { return $checkHelper(element); } + ; + } + print F <<END +}; +} END - ; - } - - print F "\n"; + ; + print F "\n"; } } @@ -686,11 +703,9 @@ sub printTypeHelpersHeaderFile print F "#ifndef ".$parameters{namespace}."ElementTypeHelpers_h\n"; print F "#define ".$parameters{namespace}."ElementTypeHelpers_h\n\n"; print F "#include \"".$parameters{namespace}."Names.h\"\n\n"; - print F "namespace WebCore {\n\n"; printTypeHelpers($F, \%allTags); - print F "}\n\n"; print F "#endif\n"; close F; @@ -703,31 +718,32 @@ sub printNamesHeaderFile open F, ">$headerPath"; printLicenseHeader($F); - printHeaderHead($F, "DOM", $parameters{namespace}, "#include \"QualifiedName.h\""); + printHeaderHead($F, "DOM", $parameters{namespace}, '#include "QualifiedName.h"', "class $parameters{namespace}QualifiedName : public QualifiedName { };\n\n"); + + my $lowercaseNamespacePrefix = lc($parameters{namespacePrefix}); - my $lowerNamespace = lc($parameters{namespacePrefix}); print F "// Namespace\n"; - print F "extern const WTF::AtomicString ${lowerNamespace}NamespaceURI;\n\n"; + print F "WEBCORE_EXPORT extern const WTF::AtomicString ${lowercaseNamespacePrefix}NamespaceURI;\n\n"; if (keys %allTags) { print F "// Tags\n"; - printMacros($F, "extern const WebCore::QualifiedName", "Tag", \%allTags); + printMacros($F, "WEBCORE_EXPORT extern const WebCore::$parameters{namespace}QualifiedName", "Tag", \%allTags); } if (keys %allAttrs) { print F "// Attributes\n"; - printMacros($F, "extern const WebCore::QualifiedName", "Attr", \%allAttrs); + printMacros($F, "WEBCORE_EXPORT extern const WebCore::QualifiedName", "Attr", \%allAttrs); } print F "#endif\n\n"; if (keys %allTags) { print F "const unsigned $parameters{namespace}TagsCount = ", scalar(keys %allTags), ";\n"; - print F "const WebCore::QualifiedName* const * get$parameters{namespace}Tags();\n"; + print F "const WebCore::$parameters{namespace}QualifiedName* const* get$parameters{namespace}Tags();\n"; } if (keys %allAttrs) { print F "const unsigned $parameters{namespace}AttrsCount = ", scalar(keys %allAttrs), ";\n"; - print F "const WebCore::QualifiedName* const * get$parameters{namespace}Attrs();\n"; + print F "const WebCore::QualifiedName* const* get$parameters{namespace}Attrs();\n"; } printInit($F, 1); @@ -743,22 +759,22 @@ sub printNamesCppFile printLicenseHeader($F); printCppHead($F, "DOM", $parameters{namespace}, "WebCore"); - my $lowerNamespace = lc($parameters{namespacePrefix}); + my $lowercaseNamespacePrefix = lc($parameters{namespacePrefix}); - print F "DEFINE_GLOBAL(AtomicString, ${lowerNamespace}NamespaceURI)\n\n"; + print F "WEBCORE_EXPORT DEFINE_GLOBAL(AtomicString, ${lowercaseNamespacePrefix}NamespaceURI)\n\n"; print F StaticString::GenerateStrings(\%allStrings); if (keys %allTags) { print F "// Tags\n"; for my $name (sort keys %allTags) { - print F "DEFINE_GLOBAL(QualifiedName, ", $name, "Tag)\n"; + print F "WEBCORE_EXPORT DEFINE_GLOBAL($parameters{namespace}QualifiedName, ", $name, "Tag)\n"; } - print F "\n\nconst WebCore::QualifiedName* const * get$parameters{namespace}Tags()\n"; - print F "{\n static const WebCore::QualifiedName* const $parameters{namespace}Tags[] = {\n"; + print F "\n\nconst WebCore::$parameters{namespace}QualifiedName* const* get$parameters{namespace}Tags()\n"; + print F "{\n static const WebCore::$parameters{namespace}QualifiedName* const $parameters{namespace}Tags[] = {\n"; for my $name (sort keys %allTags) { - print F " reinterpret_cast<const WebCore::QualifiedName*>(&${name}Tag),\n"; + print F " reinterpret_cast<const WebCore::$parameters{namespace}QualifiedName*>(&${name}Tag),\n"; } print F " };\n"; print F " return $parameters{namespace}Tags;\n"; @@ -768,9 +784,9 @@ sub printNamesCppFile if (keys %allAttrs) { print F "\n// Attributes\n"; for my $name (sort keys %allAttrs) { - print F "DEFINE_GLOBAL(QualifiedName, ", $name, "Attr)\n"; + print F "WEBCORE_EXPORT DEFINE_GLOBAL(QualifiedName, ", $name, "Attr)\n"; } - print F "\n\nconst WebCore::QualifiedName* const * get$parameters{namespace}Attrs()\n"; + print F "\n\nconst WebCore::QualifiedName* const* get$parameters{namespace}Attrs()\n"; print F "{\n static const WebCore::QualifiedName* const $parameters{namespace}Attrs[] = {\n"; for my $name (sort keys %allAttrs) { print F " reinterpret_cast<const WebCore::QualifiedName*>(&${name}Attr),\n"; @@ -782,19 +798,19 @@ sub printNamesCppFile printInit($F, 0); - print(F " AtomicString ${lowerNamespace}NS(\"$parameters{namespaceURI}\", AtomicString::ConstructFromLiteral);\n\n"); + print(F " AtomicString ${lowercaseNamespacePrefix}NS(\"$parameters{namespaceURI}\", AtomicString::ConstructFromLiteral);\n\n"); print(F " // Namespace\n"); - print(F " new (NotNull, (void*)&${lowerNamespace}NamespaceURI) AtomicString(${lowerNamespace}NS);\n"); + print(F " new (NotNull, (void*)&${lowercaseNamespacePrefix}NamespaceURI) AtomicString(${lowercaseNamespacePrefix}NS);\n"); print(F "\n"); print F StaticString::GenerateStringAsserts(\%allStrings); if (keys %allTags) { - my $tagsNamespace = $parameters{tagsNullNamespace} ? "nullAtom" : "${lowerNamespace}NS"; + my $tagsNamespace = $parameters{tagsNullNamespace} ? "nullAtom" : "${lowercaseNamespacePrefix}NS"; printDefinitions($F, \%allTags, "tags", $tagsNamespace); } if (keys %allAttrs) { - my $attrsNamespace = $parameters{attrsNullNamespace} ? "nullAtom" : "${lowerNamespace}NS"; + my $attrsNamespace = $parameters{attrsNullNamespace} ? "nullAtom" : "${lowercaseNamespacePrefix}NS"; printDefinitions($F, \%allAttrs, "attributes", $attrsNamespace); } @@ -957,17 +973,42 @@ namespace WebCore { using namespace $parameters{namespace}Names; -typedef PassRefPtr<$parameters{namespace}Element> (*$parameters{namespace}ConstructorFunction)(const QualifiedName&, Document&$formElementArgumentForDeclaration, bool createdByParser); +typedef Ref<$parameters{namespace}Element> (*$parameters{namespace}ConstructorFunction)(const QualifiedName&, Document&$formElementArgumentForDeclaration, bool createdByParser); END ; my %tagConstructorMap = buildConstructorMap(); + my $argumentList; + + if ($parameters{namespace} eq "HTML") { + $argumentList = "name, document, formElement, createdByParser"; + } else { + $argumentList = "name, document, createdByParser"; + } + + my $lowercaseNamespacePrefix = lc($parameters{namespacePrefix}); printConstructors($F, \%tagConstructorMap); print F <<END -static NEVER_INLINE void populate$parameters{namespace}FactoryMap(HashMap<AtomicStringImpl*, $parameters{namespace}ConstructorFunction>& map) + +struct ConstructorFunctionMapEntry { + ConstructorFunctionMapEntry($parameters{namespace}ConstructorFunction function, const QualifiedName& name) + : function(function) + , qualifiedName(&name) + { } + + ConstructorFunctionMapEntry() + : function(nullptr) + , qualifiedName(nullptr) + { } + + $parameters{namespace}ConstructorFunction function; + const QualifiedName* qualifiedName; // Use pointer instead of reference so that emptyValue() in HashMap is cheap to create. +}; + +static NEVER_INLINE void populate$parameters{namespace}FactoryMap(HashMap<AtomicStringImpl*, ConstructorFunctionMapEntry>& map) { struct TableEntry { const QualifiedName& name; @@ -984,45 +1025,54 @@ END }; for (unsigned i = 0; i < WTF_ARRAY_LENGTH(table); ++i) - map.add(table[i].name.localName().impl(), table[i].function); + map.add(table[i].name.localName().impl(), ConstructorFunctionMapEntry(table[i].function, table[i].name)); } -PassRefPtr<$parameters{namespace}Element> $parameters{namespace}ElementFactory::createElement(const QualifiedName& name, Document& document$formElementArgumentForDefinition, bool createdByParser) + +static ConstructorFunctionMapEntry find$parameters{namespace}ElementConstructorFunction(const AtomicString& localName) { -END - ; + static NeverDestroyed<HashMap<AtomicStringImpl*, ConstructorFunctionMapEntry>> map; + if (map.get().isEmpty()) + populate$parameters{namespace}FactoryMap(map); + return map.get().get(localName.impl()); +} - if ($parameters{namespace} ne "HTML" and $parameters{namespace} ne "SVG") { - print F <<END -#if ENABLE(DASHBOARD_SUPPORT) - Settings* settings = document.settings(); - if (settings && settings->usesDashboardBackwardCompatibilityMode()) - return 0; -#endif -END - ; +RefPtr<$parameters{namespace}Element> $parameters{namespace}ElementFactory::createKnownElement(const AtomicString& localName, Document& document$formElementArgumentForDefinition, bool createdByParser) +{ + const ConstructorFunctionMapEntry& entry = find$parameters{namespace}ElementConstructorFunction(localName); + if (LIKELY(entry.function)) { + ASSERT(entry.qualifiedName); + const auto& name = *entry.qualifiedName; + return entry.function($argumentList); } + return nullptr; +} - print F <<END - static NeverDestroyed<HashMap<AtomicStringImpl*, $parameters{namespace}ConstructorFunction>> functions; - if (functions.get().isEmpty()) - populate$parameters{namespace}FactoryMap(functions); - if ($parameters{namespace}ConstructorFunction function = functions.get().get(name.localName().impl())) { -END - ; +RefPtr<$parameters{namespace}Element> $parameters{namespace}ElementFactory::createKnownElement(const QualifiedName& name, Document& document$formElementArgumentForDefinition, bool createdByParser) +{ + const ConstructorFunctionMapEntry& entry = find$parameters{namespace}ElementConstructorFunction(name.localName()); + if (LIKELY(entry.function)) + return entry.function($argumentList); + return nullptr; +} - if ($parameters{namespace} eq "HTML") { - print F " if (RefPtr<$parameters{namespace}Element> element = function(name, document, formElement, createdByParser))\n"; - print F " return element.release();\n"; - } else { - print F " if (RefPtr<$parameters{namespace}Element> element = function(name, document, createdByParser))\n"; - print F " return element.release();\n"; +Ref<$parameters{namespace}Element> $parameters{namespace}ElementFactory::createElement(const AtomicString& localName, Document& document$formElementArgumentForDefinition, bool createdByParser) +{ + const ConstructorFunctionMapEntry& entry = find$parameters{namespace}ElementConstructorFunction(localName); + if (LIKELY(entry.function)) { + ASSERT(entry.qualifiedName); + const auto& name = *entry.qualifiedName; + return entry.function($argumentList); } + return $parameters{fallbackInterfaceName}::create(QualifiedName(nullAtom, localName, ${lowercaseNamespacePrefix}NamespaceURI), document); +} - print F " }\n"; - print F " return $parameters{fallbackInterfaceName}::create(name, document);\n"; - - print F <<END +Ref<$parameters{namespace}Element> $parameters{namespace}ElementFactory::createElement(const QualifiedName& name, Document& document$formElementArgumentForDefinition, bool createdByParser) +{ + const ConstructorFunctionMapEntry& entry = find$parameters{namespace}ElementConstructorFunction(name.localName()); + if (LIKELY(entry.function)) + return entry.function($argumentList); + return $parameters{fallbackInterfaceName}::create(name, document); } } // namespace WebCore @@ -1051,23 +1101,36 @@ sub printFactoryHeaderFile namespace WebCore { - class Document; - class HTMLFormElement; - class QualifiedName; +class Document; +class HTMLFormElement; +class QualifiedName; - class $parameters{namespace}Element; +class $parameters{namespace}Element; - class $parameters{namespace}ElementFactory { - public: +class $parameters{namespace}ElementFactory { +public: END ; -print F " static PassRefPtr<$parameters{namespace}Element> createElement(const QualifiedName&, Document&"; +print F "static RefPtr<$parameters{namespace}Element> createKnownElement(const AtomicString&, Document&"; +print F ", HTMLFormElement* = nullptr" if $parameters{namespace} eq "HTML"; +print F ", bool createdByParser = false);\n"; + +print F "static RefPtr<$parameters{namespace}Element> createKnownElement(const QualifiedName&, Document&"; +print F ", HTMLFormElement* = nullptr" if $parameters{namespace} eq "HTML"; +print F ", bool createdByParser = false);\n"; + +print F "static Ref<$parameters{namespace}Element> createElement(const AtomicString&, Document&"; +print F ", HTMLFormElement* = nullptr" if $parameters{namespace} eq "HTML"; +print F ", bool createdByParser = false);\n"; + +print F "static Ref<$parameters{namespace}Element> createElement(const QualifiedName&, Document&"; print F ", HTMLFormElement* = nullptr" if $parameters{namespace} eq "HTML"; print F ", bool createdByParser = false);\n"; printf F<<END - }; +}; + } #endif // $parameters{namespace}ElementFactory_h @@ -1096,7 +1159,7 @@ sub printWrapperFunctions for my $tagName (sort keys %enabledTags) { # Avoid defining the same wrapper method twice. my $JSInterfaceName = $enabledTags{$tagName}{JSInterfaceName}; - next if defined($tagsSeen{$JSInterfaceName}) || (usesDefaultJSWrapper($tagName) && ($parameters{fallbackJSInterfaceName} eq $parameters{namespace} . "Element")); + next if (defined($tagsSeen{$JSInterfaceName}) || (usesDefaultJSWrapper($tagName) && ($parameters{fallbackJSInterfaceName} eq $parameters{namespace} . "Element"))) && !$enabledTags{$tagName}{settingsConditional}; $tagsSeen{$JSInterfaceName} = 1; my $conditional = $enabledTags{$tagName}{conditional}; @@ -1107,11 +1170,22 @@ sub printWrapperFunctions if ($enabledTags{$tagName}{wrapperOnlyIfMediaIsAvailable}) { print F <<END -static JSDOMWrapper* create${JSInterfaceName}Wrapper(ExecState* exec, JSDOMGlobalObject* globalObject, PassRefPtr<$parameters{namespace}Element> element) +static JSDOMObject* create${JSInterfaceName}Wrapper(JSDOMGlobalObject* globalObject, Ref<$parameters{namespace}Element>&& element) { - if (element->isHTMLUnknownElement()) - return CREATE_DOM_WRAPPER(exec, globalObject, $parameters{namespace}Element, element.get()); - return CREATE_DOM_WRAPPER(exec, globalObject, ${JSInterfaceName}, element.get()); + if (element->is$parameters{fallbackInterfaceName}()) + return createWrapper<$parameters{fallbackInterfaceName}>(globalObject, WTFMove(element)); + return createWrapper<${JSInterfaceName}>(globalObject, WTFMove(element)); +} + +END + ; + } elsif ($enabledTags{$tagName}{settingsConditional}) { + print F <<END +static JSDOMObject* create$enabledTags{$tagName}{interfaceName}Wrapper(JSDOMGlobalObject* globalObject, Ref<$parameters{namespace}Element>&& element) +{ + if (element->is$parameters{fallbackInterfaceName}()) + return createWrapper<$parameters{fallbackInterfaceName}>(globalObject, WTFMove(element)); + return createWrapper<${JSInterfaceName}>(globalObject, WTFMove(element)); } END @@ -1119,22 +1193,22 @@ END } elsif ($enabledTags{$tagName}{runtimeConditional}) { my $runtimeConditional = $enabledTags{$tagName}{runtimeConditional}; print F <<END -static JSDOMWrapper* create${JSInterfaceName}Wrapper(ExecState* exec, JSDOMGlobalObject* globalObject, PassRefPtr<$parameters{namespace}Element> element) +static JSDOMObject* create${JSInterfaceName}Wrapper(JSDOMGlobalObject* globalObject, Ref<$parameters{namespace}Element>&& element) { if (!RuntimeEnabledFeatures::sharedFeatures().${runtimeConditional}Enabled()) { - ASSERT(!element || element->is$parameters{fallbackInterfaceName}()); - return CREATE_DOM_WRAPPER(exec, globalObject, $parameters{fallbackJSInterfaceName}, element.get()); + ASSERT(element->is$parameters{fallbackInterfaceName}()); + return createWrapper<$parameters{fallbackJSInterfaceName}>(globalObject, WTFMove(element)); } - return CREATE_DOM_WRAPPER(exec, globalObject, ${JSInterfaceName}, element.get()); + return createWrapper<${JSInterfaceName}>(globalObject, WTFMove(element)); } END ; } else { print F <<END -static JSDOMWrapper* create${JSInterfaceName}Wrapper(ExecState* exec, JSDOMGlobalObject* globalObject, PassRefPtr<$parameters{namespace}Element> element) +static JSDOMObject* create${JSInterfaceName}Wrapper(JSDOMGlobalObject* globalObject, Ref<$parameters{namespace}Element>&& element) { - return CREATE_DOM_WRAPPER(exec, globalObject, ${JSInterfaceName}, element.get()); + return createWrapper<${JSInterfaceName}>(globalObject, WTFMove(element)); } END @@ -1185,7 +1259,7 @@ namespace WebCore { using namespace $parameters{namespace}Names; -typedef JSDOMWrapper* (*Create$parameters{namespace}ElementWrapperFunction)(ExecState*, JSDOMGlobalObject*, PassRefPtr<$parameters{namespace}Element>); +typedef JSDOMObject* (*Create$parameters{namespace}ElementWrapperFunction)(JSDOMGlobalObject*, Ref<$parameters{namespace}Element>&&); END ; @@ -1215,11 +1289,14 @@ END print F "#if ${conditionalString}\n"; } - my $ucTag = $enabledTags{$tag}{JSInterfaceName}; + my $ucTag; + if ($enabledTags{$tag}{settingsConditional}) { + $ucTag = $enabledTags{$tag}{interfaceName}; + } else { + $ucTag = $enabledTags{$tag}{JSInterfaceName}; + } - # FIXME Remove unnecessary '&' from the following (print) line once we switch to a non-broken Visual Studio compiler. - # https://bugs.webkit.org/show_bug.cgi?id=121235: - print F " { ${tag}Tag, &create${ucTag}Wrapper },\n"; + print F " { ${tag}Tag, create${ucTag}Wrapper },\n"; if ($conditional) { print F "#endif\n"; @@ -1233,14 +1310,26 @@ END map.add(table[i].name.localName().impl(), table[i].function); } -JSDOMWrapper* createJS$parameters{namespace}Wrapper(ExecState* exec, JSDOMGlobalObject* globalObject, PassRefPtr<$parameters{namespace}Element> element) +JSDOMObject* createJS$parameters{namespace}Wrapper(JSDOMGlobalObject* globalObject, Ref<$parameters{namespace}Element>&& element) { static NeverDestroyed<HashMap<AtomicStringImpl*, Create$parameters{namespace}ElementWrapperFunction>> functions; if (functions.get().isEmpty()) populate$parameters{namespace}WrapperMap(functions); if (auto function = functions.get().get(element->localName().impl())) - return function(exec, globalObject, element); - return CREATE_DOM_WRAPPER(exec, globalObject, $parameters{fallbackJSInterfaceName}, element.get()); + return function(globalObject, WTFMove(element)); +END +; + + if ($parameters{customElementInterfaceName}) { + print F <<END + if (element->isCustomElementUpgradeCandidate()) + return createWrapper<$parameters{customElementInterfaceName}>(globalObject, WTFMove(element)); +END +; + } + + print F <<END + return createWrapper<$parameters{fallbackJSInterfaceName}>(globalObject, WTFMove(element)); } } @@ -1269,17 +1358,13 @@ sub printWrapperFactoryHeaderFile print F <<END #include <wtf/Forward.h> -namespace JSC { - class ExecState; -} - namespace WebCore { - class JSDOMWrapper; + class JSDOMObject; class JSDOMGlobalObject; class $parameters{namespace}Element; - JSDOMWrapper* createJS$parameters{namespace}Wrapper(JSC::ExecState*, JSDOMGlobalObject*, PassRefPtr<$parameters{namespace}Element>); + JSDOMObject* createJS$parameters{namespace}Wrapper(JSDOMGlobalObject*, Ref<$parameters{namespace}Element>&&); } |