Diff
Modified: trunk/Source/WebCore/ChangeLog (283879 => 283880)
--- trunk/Source/WebCore/ChangeLog 2021-10-10 22:24:10 UTC (rev 283879)
+++ trunk/Source/WebCore/ChangeLog 2021-10-10 23:05:00 UTC (rev 283880)
@@ -1,3 +1,50 @@
+2021-10-09 Dean Jackson <[email protected]>
+
+ [WebXR] Replace the session reference in WebXRSpace subclasses with weak pointers
+ https://bugs.webkit.org/show_bug.cgi?id=231482
+
+ Reviewed by Sam Weinig.
+
+ WebXRSpace had a pure virtual session() accessor that returned a
+ reference to a WebXRSession. This made subclasses hold strong
+ references to the WebXRSession, and is problematic for WebXRSpace
+ subclasses that are (indirectly) owned by the WebXRSession.
+
+ Change the accessor to return a pointer, and all the subclasses
+ to use weak pointers instead.
+
+ * Modules/webxr/WebXRBoundedReferenceSpace.h: Use a WeakPtr and change
+ the constructor parameter to take a reference instead of a Ref<>.
+ * Modules/webxr/WebXRBoundedReferenceSpace.cpp:
+ (WebCore::WebXRBoundedReferenceSpace::create):
+ (WebCore::WebXRBoundedReferenceSpace::WebXRBoundedReferenceSpace):
+ (WebCore::WebXRBoundedReferenceSpace::getOffsetReferenceSpace):
+ (WebCore::WebXRBoundedReferenceSpace::updateIfNeeded): Add early return.
+
+ * Modules/webxr/WebXRFrame.cpp:
+ (WebCore::WebXRFrame::populatePose): Compare pointers directly.
+
+ * Modules/webxr/WebXRInputSpace.h: Use WeakPtr.
+ * Modules/webxr/WebXRInputSpace.cpp:
+ (WebCore::WebXRInputSpace::WebXRInputSpace):
+ (WebCore::WebXRInputSpace::nativeOrigin const): Change this to return an
+ optional so we can detect errors and raise Exceptions.
+
+ * Modules/webxr/WebXRReferenceSpace.h: Use a WeakPtr and change
+ the constructor parameter to take a reference instead of a Ref<>.
+ * Modules/webxr/WebXRReferenceSpace.cpp:
+ (WebCore::WebXRReferenceSpace::create):
+ (WebCore::WebXRReferenceSpace::WebXRReferenceSpace):
+ (WebCore::WebXRReferenceSpace::nativeOrigin const): Return an optional.
+ (WebCore::WebXRReferenceSpace::getOffsetReferenceSpace):
+ (WebCore::WebXRReferenceSpace::floorOriginTransform const): Return an optional.
+
+ * Modules/webxr/WebXRSpace.cpp: Return a * instead of a &.
+ (WebCore::WebXRSpace::isPositionEmulated const):
+ (WebCore::WebXRViewerSpace::WebXRViewerSpace):
+ (WebCore::WebXRViewerSpace::nativeOrigin const):
+ * Modules/webxr/WebXRSpace.h:
+
2021-10-10 Alan Bujtas <[email protected]>
[IFC][Integration] Do not bail out on missing primary font
Modified: trunk/Source/WebCore/Modules/webxr/WebXRBoundedReferenceSpace.cpp (283879 => 283880)
--- trunk/Source/WebCore/Modules/webxr/WebXRBoundedReferenceSpace.cpp 2021-10-10 22:24:10 UTC (rev 283879)
+++ trunk/Source/WebCore/Modules/webxr/WebXRBoundedReferenceSpace.cpp 2021-10-10 23:05:00 UTC (rev 283880)
@@ -44,24 +44,24 @@
WTF_MAKE_ISO_ALLOCATED_IMPL(WebXRBoundedReferenceSpace);
-Ref<WebXRBoundedReferenceSpace> WebXRBoundedReferenceSpace::create(Document& document, Ref<WebXRSession>&& session, XRReferenceSpaceType type)
+Ref<WebXRBoundedReferenceSpace> WebXRBoundedReferenceSpace::create(Document& document, WebXRSession& session, XRReferenceSpaceType type)
{
- return adoptRef(*new WebXRBoundedReferenceSpace(document, WTFMove(session), WebXRRigidTransform::create(), type));
+ return adoptRef(*new WebXRBoundedReferenceSpace(document, session, WebXRRigidTransform::create(), type));
}
-Ref<WebXRBoundedReferenceSpace> WebXRBoundedReferenceSpace::create(Document& document, Ref<WebXRSession>&& session, Ref<WebXRRigidTransform>&& offset, XRReferenceSpaceType type)
+Ref<WebXRBoundedReferenceSpace> WebXRBoundedReferenceSpace::create(Document& document, WebXRSession& session, Ref<WebXRRigidTransform>&& offset, XRReferenceSpaceType type)
{
- return adoptRef(*new WebXRBoundedReferenceSpace(document, WTFMove(session), WTFMove(offset), type));
+ return adoptRef(*new WebXRBoundedReferenceSpace(document, session, WTFMove(offset), type));
}
-WebXRBoundedReferenceSpace::WebXRBoundedReferenceSpace(Document& document, Ref<WebXRSession>&& session, Ref<WebXRRigidTransform>&& offset, XRReferenceSpaceType type)
- : WebXRReferenceSpace(document, WTFMove(session), WTFMove(offset), type)
+WebXRBoundedReferenceSpace::WebXRBoundedReferenceSpace(Document& document, WebXRSession& session, Ref<WebXRRigidTransform>&& offset, XRReferenceSpaceType type)
+ : WebXRReferenceSpace(document, session, WTFMove(offset), type)
{
}
WebXRBoundedReferenceSpace::~WebXRBoundedReferenceSpace() = default;
-TransformationMatrix WebXRBoundedReferenceSpace::nativeOrigin() const
+std::optional<TransformationMatrix> WebXRBoundedReferenceSpace::nativeOrigin() const
{
// https://immersive-web.github.io/webxr/#dom-xrreferencespacetype-bounded-floor.
// Bounded floor space should be at the same height as local floor space.
@@ -76,6 +76,9 @@
ExceptionOr<Ref<WebXRReferenceSpace>> WebXRBoundedReferenceSpace::getOffsetReferenceSpace(const WebXRRigidTransform& offsetTransform)
{
+ if (!m_session)
+ return Exception { InvalidStateError };
+
auto* document = downcast<Document>(scriptExecutionContext());
if (!document)
return Exception { InvalidStateError };
@@ -84,12 +87,15 @@
// Set offsetSpace’s origin offset to the result of multiplying base’s origin offset by originOffset in the relevant realm of base.
auto offset = WebXRRigidTransform::create(originOffset().rawTransform() * offsetTransform.rawTransform());
- return { create(*document, m_session.copyRef(), WTFMove(offset), m_type) };
+ return { create(*document, *m_session.get(), WTFMove(offset), m_type) };
}
// https://immersive-web.github.io/webxr/#dom-xrboundedreferencespace-boundsgeometry
void WebXRBoundedReferenceSpace::updateIfNeeded()
{
+ if (!m_session)
+ return;
+
auto& frameData = m_session->frameData();
if (frameData.stageParameters.id == m_lastUpdateId)
return;
Modified: trunk/Source/WebCore/Modules/webxr/WebXRBoundedReferenceSpace.h (283879 => 283880)
--- trunk/Source/WebCore/Modules/webxr/WebXRBoundedReferenceSpace.h 2021-10-10 22:24:10 UTC (rev 283879)
+++ trunk/Source/WebCore/Modules/webxr/WebXRBoundedReferenceSpace.h 2021-10-10 23:05:00 UTC (rev 283880)
@@ -39,17 +39,17 @@
class WebXRBoundedReferenceSpace final : public WebXRReferenceSpace {
WTF_MAKE_ISO_ALLOCATED(WebXRBoundedReferenceSpace);
public:
- static Ref<WebXRBoundedReferenceSpace> create(Document&, Ref<WebXRSession>&&, XRReferenceSpaceType);
- static Ref<WebXRBoundedReferenceSpace> create(Document&, Ref<WebXRSession>&&, Ref<WebXRRigidTransform>&&, XRReferenceSpaceType);
+ static Ref<WebXRBoundedReferenceSpace> create(Document&, WebXRSession&, XRReferenceSpaceType);
+ static Ref<WebXRBoundedReferenceSpace> create(Document&, WebXRSession&, Ref<WebXRRigidTransform>&&, XRReferenceSpaceType);
virtual ~WebXRBoundedReferenceSpace();
- TransformationMatrix nativeOrigin() const final;
+ std::optional<TransformationMatrix> nativeOrigin() const final;
const Vector<Ref<DOMPointReadOnly>>& boundsGeometry();
ExceptionOr<Ref<WebXRReferenceSpace>> getOffsetReferenceSpace(const WebXRRigidTransform&) final;
private:
- WebXRBoundedReferenceSpace(Document&, Ref<WebXRSession>&&, Ref<WebXRRigidTransform>&&, XRReferenceSpaceType);
+ WebXRBoundedReferenceSpace(Document&, WebXRSession&, Ref<WebXRRigidTransform>&&, XRReferenceSpaceType);
bool isBoundedReferenceSpace() const final { return true; }
Modified: trunk/Source/WebCore/Modules/webxr/WebXRFrame.cpp (283879 => 283880)
--- trunk/Source/WebCore/Modules/webxr/WebXRFrame.cpp 2021-10-10 22:24:10 UTC (rev 283879)
+++ trunk/Source/WebCore/Modules/webxr/WebXRFrame.cpp 2021-10-10 23:05:00 UTC (rev 283880)
@@ -104,11 +104,11 @@
// 2. Let session be frame’s session object.
// 3. If space’s session does not equal session, throw an InvalidStateError and abort these steps.
- if (&space.session() != m_session.ptr())
+ if (space.session() != m_session.ptr())
return Exception { InvalidStateError };
// 4. If baseSpace’s session does not equal session, throw an InvalidStateError and abort these steps.
- if (&baseSpace.session() != m_session.ptr())
+ if (baseSpace.session() != m_session.ptr())
return Exception { InvalidStateError };
// 5. Check if poses may be reported and, if not, throw a SecurityError and abort these steps.
@@ -127,12 +127,28 @@
}
auto baseTransform = baseSpace.effectiveOrigin();
- if (!baseTransform.isInvertible())
+ if (!baseTransform)
+ return Exception { InvalidStateError };
+
+ if (!baseTransform.value().isInvertible())
return { std::nullopt };
- auto transform = *baseTransform.inverse() * space.effectiveOrigin();
- bool emulatedPosition = space.isPositionEmulated() || baseSpace.isPositionEmulated();
+ auto effectiveOrigin = space.effectiveOrigin();
+ if (!effectiveOrigin)
+ return Exception { InvalidStateError };
+ auto transform = *baseTransform.value().inverse() * effectiveOrigin.value();
+
+ auto isPositionEmulated = space.isPositionEmulated();
+ if (!isPositionEmulated)
+ return Exception { InvalidStateError };
+
+ auto baseSpaceIsPositionEmulated = baseSpace.isPositionEmulated();
+ if (!baseSpaceIsPositionEmulated)
+ return Exception { InvalidStateError };
+
+ bool emulatedPosition = isPositionEmulated.value() || baseSpaceIsPositionEmulated.value();
+
bool limit = mustPosesBeLimited(space, baseSpace);
if (limit) {
// FIXME: apply pose limits logic
Modified: trunk/Source/WebCore/Modules/webxr/WebXRInputSpace.cpp (283879 => 283880)
--- trunk/Source/WebCore/Modules/webxr/WebXRInputSpace.cpp 2021-10-10 22:24:10 UTC (rev 283879)
+++ trunk/Source/WebCore/Modules/webxr/WebXRInputSpace.cpp 2021-10-10 23:05:00 UTC (rev 283880)
@@ -46,7 +46,7 @@
WebXRInputSpace::WebXRInputSpace(Document& document, WebXRSession& session, const PlatformXR::Device::FrameData::InputSourcePose& pose)
: WebXRSpace(document, WebXRRigidTransform::create())
- , m_session(session)
+ , m_session(makeWeakPtr(&session))
, m_pose(pose)
{
}
@@ -53,7 +53,7 @@
WebXRInputSpace::~WebXRInputSpace() = default;
-TransformationMatrix WebXRInputSpace::nativeOrigin() const
+std::optional<TransformationMatrix> WebXRInputSpace::nativeOrigin() const
{
return WebXRFrame::matrixFromPose(m_pose.pose);
}
Modified: trunk/Source/WebCore/Modules/webxr/WebXRInputSpace.h (283879 => 283880)
--- trunk/Source/WebCore/Modules/webxr/WebXRInputSpace.h 2021-10-10 22:24:10 UTC (rev 283879)
+++ trunk/Source/WebCore/Modules/webxr/WebXRInputSpace.h 2021-10-10 23:05:00 UTC (rev 283880)
@@ -44,18 +44,18 @@
using RefCounted<WebXRInputSpace>::ref;
using RefCounted<WebXRInputSpace>::deref;
- bool isPositionEmulated() const final { return m_pose.isPositionEmulated; }
+ std::optional<bool> isPositionEmulated() const final { return m_pose.isPositionEmulated; }
void setPose(const PlatformXR::Device::FrameData::InputSourcePose& pose) { m_pose = pose; }
private:
WebXRInputSpace(Document&, WebXRSession&, const PlatformXR::Device::FrameData::InputSourcePose&);
- WebXRSession& session() const final { return m_session.get(); }
- TransformationMatrix nativeOrigin() const final;
+ WebXRSession* session() const final { return m_session.get(); }
+ std::optional<TransformationMatrix> nativeOrigin() const final;
void refEventTarget() final { ref(); }
void derefEventTarget() final { deref(); }
- Ref<WebXRSession> m_session;
+ WeakPtr<WebXRSession> m_session;
PlatformXR::Device::FrameData::InputSourcePose m_pose;
};
Modified: trunk/Source/WebCore/Modules/webxr/WebXRReferenceSpace.cpp (283879 => 283880)
--- trunk/Source/WebCore/Modules/webxr/WebXRReferenceSpace.cpp 2021-10-10 22:24:10 UTC (rev 283879)
+++ trunk/Source/WebCore/Modules/webxr/WebXRReferenceSpace.cpp 2021-10-10 23:05:00 UTC (rev 283880)
@@ -40,23 +40,23 @@
WTF_MAKE_ISO_ALLOCATED_IMPL(WebXRReferenceSpace);
-Ref<WebXRReferenceSpace> WebXRReferenceSpace::create(Document& document, Ref<WebXRSession>&& session, XRReferenceSpaceType type)
+Ref<WebXRReferenceSpace> WebXRReferenceSpace::create(Document& document, WebXRSession& session, XRReferenceSpaceType type)
{
// https://immersive-web.github.io/webxr/#xrspace-native-origin
// The transform from the effective space to the native origin's space is
// defined by an origin offset, which is an XRRigidTransform initially set
// to an identity transform.
- return adoptRef(*new WebXRReferenceSpace(document, WTFMove(session), WebXRRigidTransform::create(), type));
+ return adoptRef(*new WebXRReferenceSpace(document, session, WebXRRigidTransform::create(), type));
}
-Ref<WebXRReferenceSpace> WebXRReferenceSpace::create(Document& document, Ref<WebXRSession>&& session, Ref<WebXRRigidTransform>&& offset, XRReferenceSpaceType type)
+Ref<WebXRReferenceSpace> WebXRReferenceSpace::create(Document& document, WebXRSession& session, Ref<WebXRRigidTransform>&& offset, XRReferenceSpaceType type)
{
- return adoptRef(*new WebXRReferenceSpace(document, WTFMove(session), WTFMove(offset), type));
+ return adoptRef(*new WebXRReferenceSpace(document, session, WTFMove(offset), type));
}
-WebXRReferenceSpace::WebXRReferenceSpace(Document& document, Ref<WebXRSession>&& session, Ref<WebXRRigidTransform>&& offset, XRReferenceSpaceType type)
+WebXRReferenceSpace::WebXRReferenceSpace(Document& document, WebXRSession& session, Ref<WebXRRigidTransform>&& offset, XRReferenceSpaceType type)
: WebXRSpace(document, WTFMove(offset))
- , m_session(WTFMove(session))
+ , m_session(makeWeakPtr(session))
, m_type(type)
{
}
@@ -64,8 +64,11 @@
WebXRReferenceSpace::~WebXRReferenceSpace() = default;
-TransformationMatrix WebXRReferenceSpace::nativeOrigin() const
+std::optional<TransformationMatrix> WebXRReferenceSpace::nativeOrigin() const
{
+ if (!m_session)
+ return std::nullopt;
+
TransformationMatrix identity;
// We assume that poses got from the devices are in local space.
@@ -90,13 +93,15 @@
default:
// BoundedFloor is handled by WebXRBoundedReferenceSpace subclass
RELEASE_ASSERT_NOT_REACHED();
+ return std::nullopt;
}
-
- return identity;
}
ExceptionOr<Ref<WebXRReferenceSpace>> WebXRReferenceSpace::getOffsetReferenceSpace(const WebXRRigidTransform& offsetTransform)
{
+ if (!m_session)
+ return Exception { InvalidStateError };
+
auto* document = downcast<Document>(scriptExecutionContext());
if (!document)
return Exception { InvalidStateError };
@@ -105,11 +110,14 @@
// Set offsetSpace’s origin offset to the result of multiplying base’s origin offset by originOffset in the relevant realm of base.
auto offset = WebXRRigidTransform::create(originOffset().rawTransform() * offsetTransform.rawTransform());
- return create(*document, m_session.copyRef(), WTFMove(offset), m_type);
+ return create(*document, *m_session.get(), WTFMove(offset), m_type);
}
-TransformationMatrix WebXRReferenceSpace::floorOriginTransform() const
+std::optional<TransformationMatrix> WebXRReferenceSpace::floorOriginTransform() const
{
+ if (!m_session)
+ return std::nullopt;
+
auto& data = ""
if (!data.floorTransform) {
TransformationMatrix defautTransform;
Modified: trunk/Source/WebCore/Modules/webxr/WebXRReferenceSpace.h (283879 => 283880)
--- trunk/Source/WebCore/Modules/webxr/WebXRReferenceSpace.h 2021-10-10 22:24:10 UTC (rev 283879)
+++ trunk/Source/WebCore/Modules/webxr/WebXRReferenceSpace.h 2021-10-10 23:05:00 UTC (rev 283880)
@@ -31,6 +31,7 @@
#include "XRReferenceSpaceType.h"
#include <wtf/IsoMalloc.h>
#include <wtf/Ref.h>
+#include <wtf/WeakPtr.h>
namespace WebCore {
@@ -40,8 +41,8 @@
class WebXRReferenceSpace : public RefCounted<WebXRReferenceSpace>, public WebXRSpace {
WTF_MAKE_ISO_ALLOCATED(WebXRReferenceSpace);
public:
- static Ref<WebXRReferenceSpace> create(Document&, Ref<WebXRSession>&&, XRReferenceSpaceType);
- static Ref<WebXRReferenceSpace> create(Document&, Ref<WebXRSession>&&, Ref<WebXRRigidTransform>&&, XRReferenceSpaceType);
+ static Ref<WebXRReferenceSpace> create(Document&, WebXRSession&, XRReferenceSpaceType);
+ static Ref<WebXRReferenceSpace> create(Document&, WebXRSession&, Ref<WebXRRigidTransform>&&, XRReferenceSpaceType);
virtual ~WebXRReferenceSpace();
@@ -48,19 +49,19 @@
using RefCounted<WebXRReferenceSpace>::ref;
using RefCounted<WebXRReferenceSpace>::deref;
- WebXRSession& session() const final { return m_session.get(); }
- TransformationMatrix nativeOrigin() const override;
+ WebXRSession* session() const final { return m_session.get(); }
+ std::optional<TransformationMatrix> nativeOrigin() const override;
virtual ExceptionOr<Ref<WebXRReferenceSpace>> getOffsetReferenceSpace(const WebXRRigidTransform&);
XRReferenceSpaceType type() const { return m_type; }
protected:
- WebXRReferenceSpace(Document&, Ref<WebXRSession>&&, Ref<WebXRRigidTransform>&&, XRReferenceSpaceType);
+ WebXRReferenceSpace(Document&, WebXRSession&, Ref<WebXRRigidTransform>&&, XRReferenceSpaceType);
bool isReferenceSpace() const final { return true; }
- TransformationMatrix floorOriginTransform() const;
+ std::optional<TransformationMatrix> floorOriginTransform() const;
- Ref<WebXRSession> m_session;
+ WeakPtr<WebXRSession> m_session;
XRReferenceSpaceType m_type;
private:
Modified: trunk/Source/WebCore/Modules/webxr/WebXRSpace.cpp (283879 => 283880)
--- trunk/Source/WebCore/Modules/webxr/WebXRSpace.cpp 2021-10-10 22:24:10 UTC (rev 283879)
+++ trunk/Source/WebCore/Modules/webxr/WebXRSpace.cpp 2021-10-10 23:05:00 UTC (rev 283880)
@@ -46,17 +46,24 @@
WebXRSpace::~WebXRSpace() = default;
-TransformationMatrix WebXRSpace::effectiveOrigin() const
+std::optional<TransformationMatrix> WebXRSpace::effectiveOrigin() const
{
// https://immersive-web.github.io/webxr/#xrspace-effective-origin
// The effective origin can be obtained by multiplying origin offset and the native origin.
- return nativeOrigin() * m_originOffset->rawTransform();
+ auto origin = nativeOrigin();
+ if (!origin)
+ return std::nullopt;
+ return origin.value() * m_originOffset->rawTransform();
}
-bool WebXRSpace::isPositionEmulated() const
+std::optional<bool> WebXRSpace::isPositionEmulated() const
{
- return session().isPositionEmulated();
+ WebXRSession* xrSession = session();
+ if (!xrSession)
+ return std::nullopt;
+
+ return xrSession->isPositionEmulated();
}
WTF_MAKE_ISO_ALLOCATED_IMPL(WebXRViewerSpace);
@@ -63,15 +70,17 @@
WebXRViewerSpace::WebXRViewerSpace(Document& document, WebXRSession& session)
: WebXRSpace(document, WebXRRigidTransform::create())
- , m_session(session)
+ , m_session(makeWeakPtr(&session))
{
}
WebXRViewerSpace::~WebXRViewerSpace() = default;
-TransformationMatrix WebXRViewerSpace::nativeOrigin() const
+std::optional<TransformationMatrix> WebXRViewerSpace::nativeOrigin() const
{
- return WebXRFrame::matrixFromPose(m_session.frameData().origin);
+ if (!m_session)
+ return std::nullopt;
+ return WebXRFrame::matrixFromPose(m_session->frameData().origin);
}
Modified: trunk/Source/WebCore/Modules/webxr/WebXRSpace.h (283879 => 283880)
--- trunk/Source/WebCore/Modules/webxr/WebXRSpace.h 2021-10-10 22:24:10 UTC (rev 283879)
+++ trunk/Source/WebCore/Modules/webxr/WebXRSpace.h 2021-10-10 23:05:00 UTC (rev 283880)
@@ -30,6 +30,7 @@
#include "ContextDestructionObserver.h"
#include "EventTarget.h"
#include "TransformationMatrix.h"
+#include "WebXRSession.h"
#include <wtf/RefCounted.h>
namespace WebCore {
@@ -37,7 +38,6 @@
class Document;
class ScriptExecutionContext;
class WebXRRigidTransform;
-class WebXRSession;
class WebXRSpace : public EventTargetWithInlineData, public ContextDestructionObserver {
WTF_MAKE_ISO_ALLOCATED(WebXRSpace);
@@ -44,10 +44,10 @@
public:
virtual ~WebXRSpace();
- virtual WebXRSession& session() const = 0;
- virtual TransformationMatrix nativeOrigin() const = 0;
- TransformationMatrix effectiveOrigin() const;
- virtual bool isPositionEmulated() const;
+ virtual WebXRSession* session() const = 0;
+ virtual std::optional<TransformationMatrix> nativeOrigin() const = 0;
+ std::optional<TransformationMatrix> effectiveOrigin() const;
+ virtual std::optional<bool> isPositionEmulated() const;
virtual bool isReferenceSpace() const { return false; }
virtual bool isBoundedReferenceSpace() const { return false; }
@@ -77,13 +77,13 @@
virtual ~WebXRViewerSpace();
private:
- WebXRSession& session() const final { return m_session; }
- TransformationMatrix nativeOrigin() const final;
+ WebXRSession* session() const final { return m_session.get(); }
+ std::optional<TransformationMatrix> nativeOrigin() const final;
void refEventTarget() final { RELEASE_ASSERT_NOT_REACHED(); }
void derefEventTarget() final { RELEASE_ASSERT_NOT_REACHED(); }
- WebXRSession& m_session;
+ WeakPtr<WebXRSession> m_session;
};
} // namespace WebCore